From f034231a6a1fd5d6395206c1651de8cd9402cca3 Mon Sep 17 00:00:00 2001 From: Ed Maste Date: Fri, 23 Aug 2013 17:46:38 +0000 Subject: [PATCH] Import lldb as of SVN r188801 (A number of files not required for the FreeBSD build have been removed.) Sponsored by: DARPA, AFRL --- LICENSE.TXT | 38 + include/lldb/API/LLDB.h | 54 + include/lldb/API/SBAddress.h | 150 + include/lldb/API/SBBlock.h | 123 + include/lldb/API/SBBreakpoint.h | 175 + include/lldb/API/SBBreakpointLocation.h | 110 + include/lldb/API/SBBroadcaster.h | 97 + include/lldb/API/SBCommandInterpreter.h | 193 + include/lldb/API/SBCommandReturnObject.h | 133 + include/lldb/API/SBCommunication.h | 99 + include/lldb/API/SBCompileUnit.h | 116 + include/lldb/API/SBData.h | 180 + include/lldb/API/SBDebugger.h | 339 + include/lldb/API/SBDeclaration.h | 89 + include/lldb/API/SBDefines.h | 84 + include/lldb/API/SBError.h | 106 + include/lldb/API/SBEvent.h | 102 + include/lldb/API/SBExpressionOptions.h | 89 + include/lldb/API/SBFileSpec.h | 96 + include/lldb/API/SBFileSpecList.h | 72 + include/lldb/API/SBFrame.h | 242 + include/lldb/API/SBFunction.h | 93 + include/lldb/API/SBHostOS.h | 57 + include/lldb/API/SBInputReader.h | 97 + include/lldb/API/SBInstruction.h | 94 + include/lldb/API/SBInstructionList.h | 71 + include/lldb/API/SBLineEntry.h | 99 + include/lldb/API/SBListener.h | 135 + include/lldb/API/SBModule.h | 287 + include/lldb/API/SBModuleSpec.h | 154 + include/lldb/API/SBProcess.h | 295 + include/lldb/API/SBSection.h | 104 + include/lldb/API/SBSourceManager.h | 53 + include/lldb/API/SBStream.h | 111 + include/lldb/API/SBStringList.h | 71 + include/lldb/API/SBSymbol.h | 109 + include/lldb/API/SBSymbolContext.h | 94 + include/lldb/API/SBSymbolContextList.h | 69 + include/lldb/API/SBTarget.h | 828 + include/lldb/API/SBThread.h | 220 + include/lldb/API/SBType.h | 244 + include/lldb/API/SBTypeCategory.h | 168 + include/lldb/API/SBTypeFilter.h | 92 + include/lldb/API/SBTypeFormat.h | 84 + include/lldb/API/SBTypeNameSpecifier.h | 77 + include/lldb/API/SBTypeSummary.h | 115 + include/lldb/API/SBTypeSynthetic.h | 102 + include/lldb/API/SBValue.h | 488 + include/lldb/API/SBValueList.h | 93 + include/lldb/API/SBWatchpoint.h | 104 + include/lldb/Breakpoint/Breakpoint.h | 630 + include/lldb/Breakpoint/BreakpointID.h | 117 + include/lldb/Breakpoint/BreakpointIDList.h | 82 + include/lldb/Breakpoint/BreakpointList.h | 193 + include/lldb/Breakpoint/BreakpointLocation.h | 401 + .../Breakpoint/BreakpointLocationCollection.h | 209 + .../lldb/Breakpoint/BreakpointLocationList.h | 269 + include/lldb/Breakpoint/BreakpointOptions.h | 358 + include/lldb/Breakpoint/BreakpointResolver.h | 147 + .../Breakpoint/BreakpointResolverAddress.h | 74 + .../Breakpoint/BreakpointResolverFileLine.h | 74 + .../Breakpoint/BreakpointResolverFileRegex.h | 68 + .../lldb/Breakpoint/BreakpointResolverName.h | 122 + include/lldb/Breakpoint/BreakpointSite.h | 295 + include/lldb/Breakpoint/BreakpointSiteList.h | 216 + include/lldb/Breakpoint/Stoppoint.h | 63 + .../Breakpoint/StoppointCallbackContext.h | 58 + include/lldb/Breakpoint/StoppointLocation.h | 147 + include/lldb/Breakpoint/Watchpoint.h | 252 + include/lldb/Breakpoint/WatchpointList.h | 276 + include/lldb/Breakpoint/WatchpointOptions.h | 255 + include/lldb/Core/Address.h | 570 + include/lldb/Core/AddressRange.h | 284 + include/lldb/Core/AddressResolver.h | 89 + include/lldb/Core/AddressResolverFileLine.h | 59 + include/lldb/Core/AddressResolverName.h | 68 + include/lldb/Core/ArchSpec.h | 422 + include/lldb/Core/Baton.h | 62 + include/lldb/Core/Broadcaster.h | 475 + include/lldb/Core/ClangForward.h | 136 + include/lldb/Core/Communication.h | 413 + include/lldb/Core/Connection.h | 162 + include/lldb/Core/ConnectionFileDescriptor.h | 139 + include/lldb/Core/ConnectionMachPort.h | 92 + include/lldb/Core/ConnectionSharedMemory.h | 70 + include/lldb/Core/ConstString.h | 507 + include/lldb/Core/DataBuffer.h | 94 + include/lldb/Core/DataBufferHeap.h | 139 + include/lldb/Core/DataBufferMemoryMap.h | 160 + include/lldb/Core/DataEncoder.h | 459 + include/lldb/Core/DataExtractor.h | 1298 ++ include/lldb/Core/Debugger.h | 397 + include/lldb/Core/Disassembler.h | 422 + include/lldb/Core/EmulateInstruction.h | 641 + include/lldb/Core/Error.h | 312 + include/lldb/Core/Event.h | 217 + include/lldb/Core/FileLineResolver.h | 81 + include/lldb/Core/FileSpecList.h | 243 + include/lldb/Core/Flags.h | 253 + include/lldb/Core/History.h | 177 + include/lldb/Core/IOStreamMacros.h | 38 + include/lldb/Core/InputReader.h | 274 + include/lldb/Core/InputReaderEZ.h | 87 + include/lldb/Core/InputReaderStack.h | 58 + include/lldb/Core/Language.h | 117 + include/lldb/Core/Listener.h | 194 + include/lldb/Core/Log.h | 236 + include/lldb/Core/Mangled.h | 306 + include/lldb/Core/MappedHash.h | 552 + include/lldb/Core/Module.h | 1085 ++ include/lldb/Core/ModuleChild.h | 90 + include/lldb/Core/ModuleList.h | 556 + include/lldb/Core/ModuleSpec.h | 594 + include/lldb/Core/Opcode.h | 270 + include/lldb/Core/PluginInterface.h | 37 + include/lldb/Core/PluginManager.h | 352 + include/lldb/Core/RangeMap.h | 1543 ++ include/lldb/Core/RegisterValue.h | 406 + include/lldb/Core/RegularExpression.h | 253 + include/lldb/Core/STLUtils.h | 94 + include/lldb/Core/Scalar.h | 341 + include/lldb/Core/SearchFilter.h | 447 + include/lldb/Core/Section.h | 313 + include/lldb/Core/SourceManager.h | 196 + include/lldb/Core/State.h | 78 + include/lldb/Core/Stream.h | 612 + include/lldb/Core/StreamAsynchronousIO.h | 42 + include/lldb/Core/StreamBuffer.h | 87 + include/lldb/Core/StreamCallback.h | 47 + include/lldb/Core/StreamFile.h | 75 + include/lldb/Core/StreamString.h | 64 + include/lldb/Core/StreamTee.h | 175 + include/lldb/Core/StringList.h | 107 + include/lldb/Core/ThreadSafeSTLMap.h | 184 + include/lldb/Core/ThreadSafeValue.h | 96 + include/lldb/Core/Timer.h | 160 + include/lldb/Core/UUID.h | 109 + include/lldb/Core/UniqueCStringMap.h | 361 + include/lldb/Core/UserID.h | 130 + include/lldb/Core/UserSettingsController.h | 98 + include/lldb/Core/VMRange.h | 181 + include/lldb/Core/Value.h | 314 + include/lldb/Core/ValueObject.h | 1375 ++ include/lldb/Core/ValueObjectCast.h | 87 + include/lldb/Core/ValueObjectChild.h | 122 + include/lldb/Core/ValueObjectConstResult.h | 179 + .../lldb/Core/ValueObjectConstResultChild.h | 77 + .../lldb/Core/ValueObjectConstResultImpl.h | 96 + include/lldb/Core/ValueObjectDynamicValue.h | 133 + include/lldb/Core/ValueObjectList.h | 90 + include/lldb/Core/ValueObjectMemory.h | 91 + include/lldb/Core/ValueObjectRegister.h | 195 + .../lldb/Core/ValueObjectSyntheticFilter.h | 182 + include/lldb/Core/ValueObjectVariable.h | 90 + include/lldb/Core/dwarf.h | 64 + .../DataFormatters/CXXFormatterFunctions.h | 874 + .../lldb/DataFormatters/DataVisualization.h | 174 + include/lldb/DataFormatters/FormatCache.h | 101 + include/lldb/DataFormatters/FormatClasses.h | 128 + include/lldb/DataFormatters/FormatManager.h | 251 + include/lldb/DataFormatters/FormatNavigator.h | 690 + include/lldb/DataFormatters/TypeCategory.h | 230 + include/lldb/DataFormatters/TypeCategoryMap.h | 148 + include/lldb/DataFormatters/TypeFormat.h | 220 + include/lldb/DataFormatters/TypeSummary.h | 547 + include/lldb/DataFormatters/TypeSynthetic.h | 594 + include/lldb/Expression/ASTDumper.h | 43 + .../lldb/Expression/ASTResultSynthesizer.h | 184 + include/lldb/Expression/ASTStructExtractor.h | 156 + include/lldb/Expression/ClangASTSource.h | 530 + include/lldb/Expression/ClangExpression.h | 153 + .../lldb/Expression/ClangExpressionDeclMap.h | 698 + .../lldb/Expression/ClangExpressionParser.h | 151 + .../lldb/Expression/ClangExpressionVariable.h | 451 + include/lldb/Expression/ClangFunction.h | 652 + .../Expression/ClangPersistentVariables.h | 75 + include/lldb/Expression/ClangUserExpression.h | 432 + .../lldb/Expression/ClangUtilityFunction.h | 179 + include/lldb/Expression/DWARFExpression.h | 424 + .../lldb/Expression/ExpressionSourceCode.h | 78 + include/lldb/Expression/IRDynamicChecks.h | 169 + include/lldb/Expression/IRExecutionUnit.h | 495 + include/lldb/Expression/IRForTarget.h | 733 + include/lldb/Expression/IRInterpreter.h | 64 + include/lldb/Expression/IRMemoryMap.h | 126 + include/lldb/Expression/IRToDWARF.h | 111 + include/lldb/Expression/Materializer.h | 173 + include/lldb/Host/Condition.h | 124 + include/lldb/Host/Config.h | 35 + include/lldb/Host/DynamicLibrary.h | 51 + include/lldb/Host/Endian.h | 33 + include/lldb/Host/File.h | 500 + include/lldb/Host/FileSpec.h | 692 + include/lldb/Host/Host.h | 502 + include/lldb/Host/Mutex.h | 312 + include/lldb/Host/Predicate.h | 509 + include/lldb/Host/ProcessRunLock.h | 165 + include/lldb/Host/SocketAddress.h | 256 + include/lldb/Host/Symbols.h | 69 + include/lldb/Host/Terminal.h | 254 + include/lldb/Host/TimeValue.h | 107 + include/lldb/Host/freebsd/Config.h | 28 + include/lldb/Interpreter/Args.h | 467 + include/lldb/Interpreter/CommandCompletions.h | 307 + include/lldb/Interpreter/CommandHistory.h | 76 + include/lldb/Interpreter/CommandInterpreter.h | 486 + include/lldb/Interpreter/CommandObject.h | 608 + .../lldb/Interpreter/CommandObjectMultiword.h | 187 + .../Interpreter/CommandObjectRegexCommand.h | 81 + .../lldb/Interpreter/CommandReturnObject.h | 183 + .../Interpreter/OptionGroupArchitecture.h | 73 + include/lldb/Interpreter/OptionGroupBoolean.h | 83 + include/lldb/Interpreter/OptionGroupFile.h | 142 + include/lldb/Interpreter/OptionGroupFormat.h | 133 + .../lldb/Interpreter/OptionGroupOutputFile.h | 76 + .../lldb/Interpreter/OptionGroupPlatform.h | 120 + include/lldb/Interpreter/OptionGroupString.h | 82 + include/lldb/Interpreter/OptionGroupUInt64.h | 82 + include/lldb/Interpreter/OptionGroupUUID.h | 61 + .../OptionGroupValueObjectDisplay.h | 85 + .../lldb/Interpreter/OptionGroupVariable.h | 65 + .../lldb/Interpreter/OptionGroupWatchpoint.h | 71 + include/lldb/Interpreter/OptionValue.h | 384 + include/lldb/Interpreter/OptionValueArch.h | 139 + include/lldb/Interpreter/OptionValueArgs.h | 46 + include/lldb/Interpreter/OptionValueArray.h | 178 + include/lldb/Interpreter/OptionValueBoolean.h | 141 + .../lldb/Interpreter/OptionValueDictionary.h | 139 + .../lldb/Interpreter/OptionValueEnumeration.h | 126 + .../lldb/Interpreter/OptionValueFileSpec.h | 129 + .../Interpreter/OptionValueFileSpecList.h | 105 + include/lldb/Interpreter/OptionValueFormat.h | 107 + .../Interpreter/OptionValuePathMappings.h | 94 + .../lldb/Interpreter/OptionValueProperties.h | 265 + include/lldb/Interpreter/OptionValueRegex.h | 98 + include/lldb/Interpreter/OptionValueSInt64.h | 172 + include/lldb/Interpreter/OptionValueString.h | 227 + include/lldb/Interpreter/OptionValueUInt64.h | 134 + include/lldb/Interpreter/OptionValueUUID.h | 106 + include/lldb/Interpreter/OptionValues.h | 31 + include/lldb/Interpreter/Options.h | 487 + include/lldb/Interpreter/Property.h | 109 + include/lldb/Interpreter/PythonDataObjects.h | 233 + include/lldb/Interpreter/ScriptInterpreter.h | 519 + .../lldb/Interpreter/ScriptInterpreterNone.h | 35 + .../Interpreter/ScriptInterpreterPython.h | 407 + include/lldb/Symbol/Block.h | 496 + include/lldb/Symbol/ClangASTContext.h | 441 + include/lldb/Symbol/ClangASTImporter.h | 371 + include/lldb/Symbol/ClangASTType.h | 679 + .../Symbol/ClangExternalASTSourceCallbacks.h | 171 + .../Symbol/ClangExternalASTSourceCommon.h | 188 + include/lldb/Symbol/ClangNamespaceDecl.h | 103 + include/lldb/Symbol/CompileUnit.h | 422 + include/lldb/Symbol/DWARFCallFrameInfo.h | 150 + include/lldb/Symbol/Declaration.h | 278 + include/lldb/Symbol/FuncUnwinders.h | 106 + include/lldb/Symbol/Function.h | 638 + include/lldb/Symbol/LineEntry.h | 174 + include/lldb/Symbol/LineTable.h | 422 + include/lldb/Symbol/ObjectContainer.h | 236 + include/lldb/Symbol/ObjectFile.h | 706 + include/lldb/Symbol/Symbol.h | 317 + include/lldb/Symbol/SymbolContext.h | 566 + include/lldb/Symbol/SymbolContextScope.h | 137 + include/lldb/Symbol/SymbolFile.h | 167 + include/lldb/Symbol/SymbolVendor.h | 207 + include/lldb/Symbol/Symtab.h | 160 + include/lldb/Symbol/TaggedASTType.h | 61 + include/lldb/Symbol/Type.h | 596 + include/lldb/Symbol/TypeList.h | 88 + include/lldb/Symbol/TypeVendor.h | 61 + include/lldb/Symbol/UnwindPlan.h | 499 + include/lldb/Symbol/UnwindTable.h | 69 + include/lldb/Symbol/Variable.h | 184 + include/lldb/Symbol/VariableList.h | 95 + include/lldb/Symbol/VerifyDecl.h | 20 + include/lldb/Target/ABI.h | 137 + include/lldb/Target/CPPLanguageRuntime.h | 158 + include/lldb/Target/DynamicLoader.h | 239 + include/lldb/Target/ExecutionContext.h | 778 + include/lldb/Target/ExecutionContextScope.h | 74 + include/lldb/Target/LanguageRuntime.h | 113 + include/lldb/Target/Memory.h | 196 + include/lldb/Target/ObjCLanguageRuntime.h | 604 + include/lldb/Target/OperatingSystem.h | 101 + include/lldb/Target/PathMappingList.h | 171 + include/lldb/Target/Platform.h | 755 + include/lldb/Target/Process.h | 3786 +++++ include/lldb/Target/RegisterContext.h | 213 + include/lldb/Target/SectionLoadList.h | 89 + include/lldb/Target/StackFrame.h | 207 + include/lldb/Target/StackFrameList.h | 157 + include/lldb/Target/StackID.h | 149 + include/lldb/Target/StopInfo.h | 227 + include/lldb/Target/Target.h | 1223 ++ include/lldb/Target/TargetList.h | 239 + include/lldb/Target/Thread.h | 1067 ++ include/lldb/Target/ThreadList.h | 163 + include/lldb/Target/ThreadPlan.h | 658 + include/lldb/Target/ThreadPlanBase.h | 71 + include/lldb/Target/ThreadPlanCallFunction.h | 193 + .../Target/ThreadPlanCallUserExpression.h | 65 + include/lldb/Target/ThreadPlanRunToAddress.h | 85 + .../lldb/Target/ThreadPlanShouldStopHere.h | 94 + include/lldb/Target/ThreadPlanStepInRange.h | 110 + .../lldb/Target/ThreadPlanStepInstruction.h | 64 + include/lldb/Target/ThreadPlanStepOut.h | 90 + .../Target/ThreadPlanStepOverBreakpoint.h | 57 + include/lldb/Target/ThreadPlanStepOverRange.h | 52 + include/lldb/Target/ThreadPlanStepRange.h | 94 + include/lldb/Target/ThreadPlanStepThrough.h | 71 + include/lldb/Target/ThreadPlanStepUntil.h | 80 + include/lldb/Target/ThreadPlanTracer.h | 131 + include/lldb/Target/ThreadSpec.h | 155 + include/lldb/Target/UnixSignals.h | 144 + include/lldb/Target/Unwind.h | 120 + include/lldb/Target/UnwindAssembly.h | 58 + include/lldb/Utility/AnsiTerminal.h | 156 + include/lldb/Utility/CleanUp.h | 322 + include/lldb/Utility/PriorityPointerPair.h | 150 + include/lldb/Utility/PseudoTerminal.h | 266 + include/lldb/Utility/PythonPointer.h | 77 + include/lldb/Utility/Range.h | 89 + include/lldb/Utility/RefCounter.h | 56 + include/lldb/Utility/SharedCluster.h | 108 + include/lldb/Utility/SharingPtr.h | 816 + include/lldb/Utility/Utils.h | 22 + include/lldb/lldb-defines.h | 125 + include/lldb/lldb-enumerations.h | 685 + include/lldb/lldb-forward.h | 378 + include/lldb/lldb-private-enumerations.h | 245 + include/lldb/lldb-private-interfaces.h | 46 + include/lldb/lldb-private-log.h | 90 + include/lldb/lldb-private-types.h | 74 + include/lldb/lldb-private.h | 84 + include/lldb/lldb-public.h | 18 + include/lldb/lldb-python.h | 29 + include/lldb/lldb-types.h | 83 + include/lldb/lldb-versioning.h | 1607 ++ source/API/SBAddress.cpp | 326 + source/API/SBBlock.cpp | 373 + source/API/SBBreakpoint.cpp | 648 + source/API/SBBreakpointLocation.cpp | 320 + source/API/SBBroadcaster.cpp | 196 + source/API/SBCommandInterpreter.cpp | 490 + source/API/SBCommandReturnObject.cpp | 352 + source/API/SBCommunication.cpp | 285 + source/API/SBCompileUnit.cpp | 278 + source/API/SBData.cpp | 785 + source/API/SBDebugger.cpp | 1264 ++ source/API/SBDeclaration.cpp | 206 + source/API/SBError.cpp | 233 + source/API/SBEvent.cpp | 245 + source/API/SBExpressionOptions.cpp | 126 + source/API/SBFileSpec.cpp | 181 + source/API/SBFileSpecList.cpp | 142 + source/API/SBFrame.cpp | 1526 ++ source/API/SBFunction.cpp | 225 + source/API/SBHostOS.cpp | 85 + source/API/SBInputReader.cpp | 216 + source/API/SBInstruction.cpp | 248 + source/API/SBInstructionList.cpp | 132 + source/API/SBLineEntry.cpp | 250 + source/API/SBListener.cpp | 443 + source/API/SBModule.cpp | 655 + source/API/SBModuleSpec.cpp | 230 + source/API/SBProcess.cpp | 1256 ++ source/API/SBSection.cpp | 291 + source/API/SBSourceManager.cpp | 146 + source/API/SBStream.cpp | 187 + source/API/SBStringList.cpp | 136 + source/API/SBSymbol.cpp | 223 + source/API/SBSymbolContext.cpp | 285 + source/API/SBSymbolContextList.cpp | 117 + source/API/SBTarget.cpp | 2660 +++ source/API/SBThread.cpp | 1229 ++ source/API/SBType.cpp | 648 + source/API/SBTypeCategory.cpp | 576 + source/API/SBTypeFilter.cpp | 199 + source/API/SBTypeFormat.cpp | 155 + source/API/SBTypeNameSpecifier.cpp | 150 + source/API/SBTypeSummary.cpp | 335 + source/API/SBTypeSynthetic.cpp | 209 + source/API/SBValue.cpp | 1719 ++ source/API/SBValueList.cpp | 275 + source/API/SBWatchpoint.cpp | 298 + source/Breakpoint/Breakpoint.cpp | 794 + source/Breakpoint/BreakpointID.cpp | 123 + source/Breakpoint/BreakpointIDList.cpp | 397 + source/Breakpoint/BreakpointList.cpp | 243 + source/Breakpoint/BreakpointLocation.cpp | 677 + .../BreakpointLocationCollection.cpp | 198 + source/Breakpoint/BreakpointLocationList.cpp | 305 + source/Breakpoint/BreakpointOptions.cpp | 298 + source/Breakpoint/BreakpointResolver.cpp | 61 + .../Breakpoint/BreakpointResolverAddress.cpp | 111 + .../Breakpoint/BreakpointResolverFileLine.cpp | 246 + .../BreakpointResolverFileRegex.cpp | 134 + source/Breakpoint/BreakpointResolverName.cpp | 357 + source/Breakpoint/BreakpointSite.cpp | 234 + source/Breakpoint/BreakpointSiteList.cpp | 240 + source/Breakpoint/Stoppoint.cpp | 46 + .../Breakpoint/StoppointCallbackContext.cpp | 39 + source/Breakpoint/StoppointLocation.cpp | 48 + source/Breakpoint/Watchpoint.cpp | 489 + source/Breakpoint/WatchpointList.cpp | 306 + source/Breakpoint/WatchpointOptions.cpp | 241 + source/Commands/CommandCompletions.cpp | 754 + source/Commands/CommandObjectApropos.cpp | 154 + source/Commands/CommandObjectApropos.h | 44 + source/Commands/CommandObjectArgs.cpp | 272 + source/Commands/CommandObjectArgs.h | 72 + source/Commands/CommandObjectBreakpoint.cpp | 1843 +++ source/Commands/CommandObjectBreakpoint.h | 47 + .../CommandObjectBreakpointCommand.cpp | 915 ++ .../Commands/CommandObjectBreakpointCommand.h | 46 + source/Commands/CommandObjectCommands.cpp | 2021 +++ source/Commands/CommandObjectCommands.h | 40 + source/Commands/CommandObjectDisassemble.cpp | 574 + source/Commands/CommandObjectDisassemble.h | 110 + source/Commands/CommandObjectExpression.cpp | 505 + source/Commands/CommandObjectExpression.h | 99 + source/Commands/CommandObjectFrame.cpp | 624 + source/Commands/CommandObjectFrame.h | 40 + source/Commands/CommandObjectHelp.cpp | 249 + source/Commands/CommandObjectHelp.h | 119 + source/Commands/CommandObjectLog.cpp | 503 + source/Commands/CommandObjectLog.h | 48 + source/Commands/CommandObjectMemory.cpp | 1370 ++ source/Commands/CommandObjectMemory.h | 33 + source/Commands/CommandObjectMultiword.cpp | 520 + source/Commands/CommandObjectPlatform.cpp | 987 ++ source/Commands/CommandObjectPlatform.h | 40 + source/Commands/CommandObjectPlugin.cpp | 122 + source/Commands/CommandObjectPlugin.h | 36 + source/Commands/CommandObjectProcess.cpp | 1945 +++ source/Commands/CommandObjectProcess.h | 37 + source/Commands/CommandObjectQuit.cpp | 99 + source/Commands/CommandObjectQuit.h | 46 + source/Commands/CommandObjectRegister.cpp | 499 + source/Commands/CommandObjectRegister.h | 45 + source/Commands/CommandObjectSettings.cpp | 1208 ++ source/Commands/CommandObjectSettings.h | 41 + source/Commands/CommandObjectSource.cpp | 925 ++ source/Commands/CommandObjectSource.h | 40 + source/Commands/CommandObjectSyntax.cpp | 113 + source/Commands/CommandObjectSyntax.h | 44 + source/Commands/CommandObjectTarget.cpp | 5354 ++++++ source/Commands/CommandObjectTarget.h | 41 + source/Commands/CommandObjectThread.cpp | 1526 ++ source/Commands/CommandObjectThread.h | 34 + source/Commands/CommandObjectType.cpp | 4112 +++++ source/Commands/CommandObjectType.h | 37 + source/Commands/CommandObjectVersion.cpp | 53 + source/Commands/CommandObjectVersion.h | 43 + source/Commands/CommandObjectWatchpoint.cpp | 1394 ++ source/Commands/CommandObjectWatchpoint.h | 43 + .../CommandObjectWatchpointCommand.cpp | 850 + .../Commands/CommandObjectWatchpointCommand.h | 46 + source/Core/Address.cpp | 1045 ++ source/Core/AddressRange.cpp | 208 + source/Core/AddressResolver.cpp | 67 + source/Core/AddressResolverFileLine.cpp | 102 + source/Core/AddressResolverName.cpp | 255 + source/Core/ArchSpec.cpp | 893 + source/Core/Baton.cpp | 24 + source/Core/Broadcaster.cpp | 499 + source/Core/Communication.cpp | 431 + source/Core/Connection.cpp | 24 + source/Core/ConnectionFileDescriptor.cpp | 1528 ++ source/Core/ConnectionMachPort.cpp | 323 + source/Core/ConnectionSharedMemory.cpp | 131 + source/Core/ConstString.cpp | 342 + source/Core/DataBufferHeap.cpp | 111 + source/Core/DataBufferMemoryMap.cpp | 258 + source/Core/DataEncoder.cpp | 335 + source/Core/DataExtractor.cpp | 2179 +++ source/Core/Debugger.cpp | 2695 +++ source/Core/Disassembler.cpp | 1269 ++ source/Core/DynamicLoader.cpp | 76 + source/Core/EmulateInstruction.cpp | 670 + source/Core/Error.cpp | 399 + source/Core/Event.cpp | 225 + source/Core/FileLineResolver.cpp | 117 + source/Core/FileSpecList.cpp | 234 + source/Core/History.cpp | 26 + source/Core/InputReader.cpp | 387 + source/Core/InputReaderEZ.cpp | 91 + source/Core/InputReaderStack.cpp | 80 + source/Core/Language.cpp | 151 + source/Core/Listener.cpp | 557 + source/Core/Log.cpp | 529 + source/Core/Mangled.cpp | 313 + source/Core/Module.cpp | 1609 ++ source/Core/ModuleChild.cpp | 46 + source/Core/ModuleList.cpp | 1103 ++ source/Core/Opcode.cpp | 134 + source/Core/PluginManager.cpp | 2064 +++ source/Core/RegisterValue.cpp | 1272 ++ source/Core/RegularExpression.cpp | 279 + source/Core/Scalar.cpp | 2279 +++ source/Core/SearchFilter.cpp | 816 + source/Core/Section.cpp | 562 + source/Core/SourceManager.cpp | 651 + source/Core/State.cpp | 115 + source/Core/Stream.cpp | 786 + source/Core/StreamAsynchronousIO.cpp | 52 + source/Core/StreamCallback.cpp | 64 + source/Core/StreamFile.cpp | 72 + source/Core/StreamString.cpp | 100 + source/Core/StringList.cpp | 290 + source/Core/Timer.cpp | 250 + source/Core/UUID.cpp | 279 + source/Core/UserID.cpp | 23 + source/Core/UserSettingsController.cpp | 111 + source/Core/VMRange.cpp | 112 + source/Core/Value.cpp | 761 + source/Core/ValueObject.cpp | 4199 +++++ source/Core/ValueObjectCast.cpp | 129 + source/Core/ValueObjectChild.cpp | 234 + source/Core/ValueObjectConstResult.cpp | 354 + source/Core/ValueObjectConstResultChild.cpp | 80 + source/Core/ValueObjectConstResultImpl.cpp | 236 + source/Core/ValueObjectDynamicValue.cpp | 372 + source/Core/ValueObjectList.cpp | 166 + source/Core/ValueObjectMemory.cpp | 275 + source/Core/ValueObjectRegister.cpp | 431 + source/Core/ValueObjectSyntheticFilter.cpp | 270 + source/Core/ValueObjectVariable.cpp | 386 + source/DataFormatters/CF.cpp | 299 + .../DataFormatters/CXXFormatterFunctions.cpp | 1351 ++ source/DataFormatters/Cocoa.cpp | 565 + source/DataFormatters/DataVisualization.cpp | 279 + source/DataFormatters/FormatCache.cpp | 169 + source/DataFormatters/FormatClasses.cpp | 33 + source/DataFormatters/FormatManager.cpp | 1083 ++ source/DataFormatters/LibCxx.cpp | 519 + source/DataFormatters/LibCxxList.cpp | 310 + source/DataFormatters/LibCxxMap.cpp | 409 + source/DataFormatters/LibStdcpp.cpp | 331 + source/DataFormatters/NSArray.cpp | 371 + source/DataFormatters/NSDictionary.cpp | 579 + source/DataFormatters/NSSet.cpp | 513 + source/DataFormatters/TypeCategory.cpp | 382 + source/DataFormatters/TypeCategoryMap.cpp | 285 + source/DataFormatters/TypeFormat.cpp | 52 + source/DataFormatters/TypeSummary.cpp | 250 + source/DataFormatters/TypeSynthetic.cpp | 114 + source/Expression/ASTDumper.cpp | 132 + source/Expression/ASTResultSynthesizer.cpp | 512 + source/Expression/ASTStructExtractor.cpp | 220 + source/Expression/ClangASTSource.cpp | 1866 +++ source/Expression/ClangExpressionDeclMap.cpp | 1956 +++ source/Expression/ClangExpressionParser.cpp | 595 + source/Expression/ClangExpressionVariable.cpp | 136 + source/Expression/ClangFunction.cpp | 633 + .../Expression/ClangPersistentVariables.cpp | 89 + source/Expression/ClangUserExpression.cpp | 1091 ++ source/Expression/ClangUtilityFunction.cpp | 169 + source/Expression/DWARFExpression.cpp | 2691 +++ source/Expression/ExpressionSourceCode.cpp | 142 + source/Expression/IRDynamicChecks.cpp | 659 + source/Expression/IRExecutionUnit.cpp | 704 + source/Expression/IRForTarget.cpp | 2879 ++++ source/Expression/IRInterpreter.cpp | 1379 ++ source/Expression/IRMemoryMap.cpp | 758 + source/Expression/Materializer.cpp | 1414 ++ source/Host/common/Condition.cpp | 106 + source/Host/common/DynamicLibrary.cpp | 33 + source/Host/common/File.cpp | 716 + source/Host/common/FileSpec.cpp | 1059 ++ source/Host/common/Host.cpp | 1568 ++ source/Host/common/Mutex.cpp | 390 + source/Host/common/SocketAddress.cpp | 260 + source/Host/common/Symbols.cpp | 163 + source/Host/common/Terminal.cpp | 307 + source/Host/common/TimeValue.cpp | 210 + source/Host/freebsd/Host.cpp | 337 + source/Interpreter/Args.cpp | 1789 ++ source/Interpreter/CommandHistory.cpp | 143 + source/Interpreter/CommandInterpreter.cpp | 2882 ++++ source/Interpreter/CommandObject.cpp | 1174 ++ .../Interpreter/CommandObjectRegexCommand.cpp | 150 + source/Interpreter/CommandObjectScript.cpp | 93 + source/Interpreter/CommandObjectScript.h | 42 + source/Interpreter/CommandReturnObject.cpp | 219 + .../Interpreter/OptionGroupArchitecture.cpp | 86 + source/Interpreter/OptionGroupBoolean.cpp | 67 + source/Interpreter/OptionGroupFile.cpp | 97 + source/Interpreter/OptionGroupFormat.cpp | 249 + source/Interpreter/OptionGroupOutputFile.cpp | 81 + source/Interpreter/OptionGroupPlatform.cpp | 149 + source/Interpreter/OptionGroupString.cpp | 58 + source/Interpreter/OptionGroupUInt64.cpp | 58 + source/Interpreter/OptionGroupUUID.cpp | 76 + .../OptionGroupValueObjectDisplay.cpp | 181 + source/Interpreter/OptionGroupVariable.cpp | 144 + source/Interpreter/OptionGroupWatchpoint.cpp | 121 + source/Interpreter/OptionValue.cpp | 633 + source/Interpreter/OptionValueArch.cpp | 111 + source/Interpreter/OptionValueArgs.cpp | 38 + source/Interpreter/OptionValueArray.cpp | 350 + source/Interpreter/OptionValueBoolean.cpp | 135 + source/Interpreter/OptionValueDictionary.cpp | 436 + source/Interpreter/OptionValueEnumeration.cpp | 166 + source/Interpreter/OptionValueFileSpec.cpp | 159 + .../Interpreter/OptionValueFileSpecLIst.cpp | 186 + source/Interpreter/OptionValueFormat.cpp | 78 + .../Interpreter/OptionValuePathMappings.cpp | 185 + source/Interpreter/OptionValueProperties.cpp | 762 + source/Interpreter/OptionValueRegex.cpp | 86 + source/Interpreter/OptionValueSInt64.cpp | 89 + source/Interpreter/OptionValueString.cpp | 186 + source/Interpreter/OptionValueUInt64.cpp | 89 + source/Interpreter/OptionValueUUID.cpp | 123 + source/Interpreter/Options.cpp | 1077 ++ source/Interpreter/Property.cpp | 275 + source/Interpreter/PythonDataObjects.cpp | 430 + source/Interpreter/ScriptInterpreter.cpp | 105 + source/Interpreter/ScriptInterpreterNone.cpp | 43 + .../Interpreter/ScriptInterpreterPython.cpp | 3166 ++++ source/Interpreter/embedded_interpreter.py | 103 + .../Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp | 861 + source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h | 138 + .../ABI/MacOSX-i386/ABIMacOSX_i386.cpp | 977 ++ .../Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h | 139 + .../ABI/SysV-x86_64/ABISysV_x86_64.cpp | 1288 ++ .../Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h | 138 + .../Disassembler/llvm/DisassemblerLLVMC.cpp | 864 + .../Disassembler/llvm/DisassemblerLLVMC.h | 166 + .../DynamicLoader/POSIX-DYLD/AuxVector.cpp | 177 + .../DynamicLoader/POSIX-DYLD/AuxVector.h | 115 + .../POSIX-DYLD/DYLDRendezvous.cpp | 336 + .../DynamicLoader/POSIX-DYLD/DYLDRendezvous.h | 230 + .../POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp | 481 + .../POSIX-DYLD/DynamicLoaderPOSIXDYLD.h | 170 + .../Static/DynamicLoaderStatic.cpp | 209 + .../Static/DynamicLoaderStatic.h | 88 + .../Instruction/ARM/EmulateInstructionARM.cpp | 13625 ++++++++++++++++ .../Instruction/ARM/EmulateInstructionARM.h | 990 ++ .../Instruction/ARM/EmulationStateARM.cpp | 406 + .../Instruction/ARM/EmulationStateARM.h | 100 + .../ItaniumABI/ItaniumABILanguageRuntime.cpp | 461 + .../ItaniumABI/ItaniumABILanguageRuntime.h | 101 + .../BSD-Archive/ObjectContainerBSDArchive.cpp | 585 + .../BSD-Archive/ObjectContainerBSDArchive.h | 229 + source/Plugins/ObjectFile/ELF/ELFHeader.cpp | 465 + source/Plugins/ObjectFile/ELF/ELFHeader.h | 433 + .../Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 1893 +++ source/Plugins/ObjectFile/ELF/ObjectFileELF.h | 333 + .../Python/OperatingSystemPython.cpp | 417 + .../Python/OperatingSystemPython.h | 109 + .../Platform/FreeBSD/PlatformFreeBSD.cpp | 648 + .../Platform/FreeBSD/PlatformFreeBSD.h | 162 + .../gdb-server/PlatformRemoteGDBServer.cpp | 418 + .../gdb-server/PlatformRemoteGDBServer.h | 147 + .../Process/FreeBSD/ProcessFreeBSD.cpp | 132 + .../Plugins/Process/FreeBSD/ProcessFreeBSD.h | 82 + .../Process/FreeBSD/ProcessMonitor.cpp | 1677 ++ .../Plugins/Process/FreeBSD/ProcessMonitor.h | 322 + .../Plugins/Process/POSIX/POSIXStopInfo.cpp | 89 + source/Plugins/Process/POSIX/POSIXStopInfo.h | 120 + source/Plugins/Process/POSIX/POSIXThread.cpp | 578 + source/Plugins/Process/POSIX/POSIXThread.h | 133 + .../Plugins/Process/POSIX/ProcessMessage.cpp | 258 + source/Plugins/Process/POSIX/ProcessMessage.h | 207 + source/Plugins/Process/POSIX/ProcessPOSIX.cpp | 911 ++ source/Plugins/Process/POSIX/ProcessPOSIX.h | 211 + .../Plugins/Process/POSIX/ProcessPOSIXLog.cpp | 193 + .../Plugins/Process/POSIX/ProcessPOSIXLog.h | 111 + .../POSIX/RegisterContextFreeBSD_x86_64.cpp | 136 + .../POSIX/RegisterContextFreeBSD_x86_64.h | 32 + .../POSIX/RegisterContextLinux_x86_64.cpp | 180 + .../POSIX/RegisterContextLinux_x86_64.h | 32 + .../Process/POSIX/RegisterContextPOSIX.h | 70 + .../Process/POSIX/RegisterContext_i386.cpp | 551 + .../Process/POSIX/RegisterContext_i386.h | 169 + .../Process/POSIX/RegisterContext_x86.h | 110 + .../Process/POSIX/RegisterContext_x86_64.cpp | 1563 ++ .../Process/POSIX/RegisterContext_x86_64.h | 347 + source/Plugins/Process/Utility/ARMDefines.h | 110 + source/Plugins/Process/Utility/ARMUtils.h | 394 + .../Process/Utility/DynamicRegisterInfo.cpp | 279 + .../Process/Utility/DynamicRegisterInfo.h | 85 + .../Process/Utility/InferiorCallPOSIX.cpp | 274 + .../Process/Utility/InferiorCallPOSIX.h | 43 + .../Process/Utility/InstructionUtils.h | 136 + .../Utility/RegisterContextDarwin_arm.cpp | 1226 ++ .../Utility/RegisterContextDarwin_arm.h | 333 + .../Utility/RegisterContextDarwin_i386.cpp | 980 ++ .../Utility/RegisterContextDarwin_i386.h | 269 + .../Utility/RegisterContextDarwin_x86_64.cpp | 1066 ++ .../Utility/RegisterContextDarwin_x86_64.h | 274 + .../Process/Utility/RegisterContextDummy.cpp | 137 + .../Process/Utility/RegisterContextDummy.h | 77 + .../Process/Utility/RegisterContextLLDB.cpp | 1541 ++ .../Process/Utility/RegisterContextLLDB.h | 212 + .../RegisterContextMacOSXFrameBackchain.cpp | 206 + .../RegisterContextMacOSXFrameBackchain.h | 77 + .../Utility/RegisterContextMach_arm.cpp | 87 + .../Process/Utility/RegisterContextMach_arm.h | 56 + .../Utility/RegisterContextMach_i386.cpp | 72 + .../Utility/RegisterContextMach_i386.h | 49 + .../Utility/RegisterContextMach_x86_64.cpp | 72 + .../Utility/RegisterContextMach_x86_64.h | 49 + .../Process/Utility/RegisterContextMemory.cpp | 174 + .../Process/Utility/RegisterContextMemory.h | 102 + .../Utility/RegisterContextThreadMemory.cpp | 261 + .../Utility/RegisterContextThreadMemory.h | 114 + .../Process/Utility/StopInfoMachException.cpp | 482 + .../Process/Utility/StopInfoMachException.h | 77 + .../Plugins/Process/Utility/ThreadMemory.cpp | 140 + source/Plugins/Process/Utility/ThreadMemory.h | 152 + source/Plugins/Process/Utility/UnwindLLDB.cpp | 322 + source/Plugins/Process/Utility/UnwindLLDB.h | 125 + .../Utility/UnwindMacOSXFrameBackchain.cpp | 275 + .../Utility/UnwindMacOSXFrameBackchain.h | 74 + .../Process/elf-core/ProcessElfCore.cpp | 619 + .../Plugins/Process/elf-core/ProcessElfCore.h | 171 + .../RegisterContextCoreFreeBSD_x86_64.cpp | 68 + .../RegisterContextCoreFreeBSD_x86_64.h | 47 + .../RegisterContextCoreLinux_x86_64.cpp | 68 + .../RegisterContextCoreLinux_x86_64.h | 54 + .../Process/elf-core/ThreadElfCore.cpp | 176 + .../Plugins/Process/elf-core/ThreadElfCore.h | 174 + .../gdb-remote/GDBRemoteCommunication.cpp | 643 + .../gdb-remote/GDBRemoteCommunication.h | 268 + .../GDBRemoteCommunicationClient.cpp | 2348 +++ .../gdb-remote/GDBRemoteCommunicationClient.h | 430 + .../GDBRemoteCommunicationServer.cpp | 839 + .../gdb-remote/GDBRemoteCommunicationServer.h | 147 + .../gdb-remote/GDBRemoteRegisterContext.cpp | 971 ++ .../gdb-remote/GDBRemoteRegisterContext.h | 311 + .../Process/gdb-remote/ProcessGDBRemote.cpp | 3291 ++++ .../Process/gdb-remote/ProcessGDBRemote.h | 396 + .../gdb-remote/ProcessGDBRemoteLog.cpp | 196 + .../Process/gdb-remote/ProcessGDBRemoteLog.h | 57 + .../Process/gdb-remote/ThreadGDBRemote.cpp | 214 + .../Process/gdb-remote/ThreadGDBRemote.h | 107 + .../DWARF/DWARFAbbreviationDeclaration.cpp | 211 + .../DWARF/DWARFAbbreviationDeclaration.h | 81 + .../Plugins/SymbolFile/DWARF/DWARFAttribute.h | 45 + .../SymbolFile/DWARF/DWARFCompileUnit.cpp | 1027 ++ .../SymbolFile/DWARF/DWARFCompileUnit.h | 210 + .../SymbolFile/DWARF/DWARFDIECollection.cpp | 62 + .../SymbolFile/DWARF/DWARFDIECollection.h | 51 + .../SymbolFile/DWARF/DWARFDebugAbbrev.cpp | 202 + .../SymbolFile/DWARF/DWARFDebugAbbrev.h | 74 + .../SymbolFile/DWARF/DWARFDebugArangeSet.cpp | 274 + .../SymbolFile/DWARF/DWARFDebugArangeSet.h | 76 + .../SymbolFile/DWARF/DWARFDebugAranges.cpp | 177 + .../SymbolFile/DWARF/DWARFDebugAranges.h | 94 + .../SymbolFile/DWARF/DWARFDebugInfo.cpp | 797 + .../Plugins/SymbolFile/DWARF/DWARFDebugInfo.h | 89 + .../SymbolFile/DWARF/DWARFDebugInfoEntry.cpp | 2317 +++ .../SymbolFile/DWARF/DWARFDebugInfoEntry.h | 457 + .../SymbolFile/DWARF/DWARFDebugLine.cpp | 1436 ++ .../Plugins/SymbolFile/DWARF/DWARFDebugLine.h | 225 + .../SymbolFile/DWARF/DWARFDebugMacinfo.cpp | 48 + .../SymbolFile/DWARF/DWARFDebugMacinfo.h | 29 + .../DWARF/DWARFDebugMacinfoEntry.cpp | 132 + .../SymbolFile/DWARF/DWARFDebugMacinfoEntry.h | 57 + .../SymbolFile/DWARF/DWARFDebugPubnames.cpp | 296 + .../SymbolFile/DWARF/DWARFDebugPubnames.h | 38 + .../DWARF/DWARFDebugPubnamesSet.cpp | 166 + .../SymbolFile/DWARF/DWARFDebugPubnamesSet.h | 99 + .../SymbolFile/DWARF/DWARFDebugRanges.cpp | 192 + .../SymbolFile/DWARF/DWARFDebugRanges.h | 46 + .../SymbolFile/DWARF/DWARFDeclContext.cpp | 104 + .../SymbolFile/DWARF/DWARFDeclContext.h | 109 + .../Plugins/SymbolFile/DWARF/DWARFDefines.cpp | 497 + .../Plugins/SymbolFile/DWARF/DWARFDefines.h | 116 + .../SymbolFile/DWARF/DWARFFormValue.cpp | 599 + .../Plugins/SymbolFile/DWARF/DWARFFormValue.h | 81 + .../DWARF/DWARFLocationDescription.cpp | 172 + .../DWARF/DWARFLocationDescription.h | 24 + .../SymbolFile/DWARF/DWARFLocationList.cpp | 94 + .../SymbolFile/DWARF/DWARFLocationList.h | 34 + .../SymbolFile/DWARF/HashedNameToDIE.h | 933 ++ .../SymbolFile/DWARF/LogChannelDWARF.cpp | 232 + .../SymbolFile/DWARF/LogChannelDWARF.h | 89 + source/Plugins/SymbolFile/DWARF/NameToDIE.cpp | 87 + source/Plugins/SymbolFile/DWARF/NameToDIE.h | 65 + .../SymbolFile/DWARF/SymbolFileDWARF.cpp | 7973 +++++++++ .../SymbolFile/DWARF/SymbolFileDWARF.h | 622 + .../DWARF/SymbolFileDWARFDebugMap.cpp | 1586 ++ .../DWARF/SymbolFileDWARFDebugMap.h | 420 + .../SymbolFile/DWARF/UniqueDWARFASTType.cpp | 94 + .../SymbolFile/DWARF/UniqueDWARFASTType.h | 175 + .../SymbolFile/Symtab/SymbolFileSymtab.cpp | 407 + .../SymbolFile/Symtab/SymbolFileSymtab.h | 142 + .../SymbolVendor/ELF/SymbolVendorELF.cpp | 199 + .../SymbolVendor/ELF/SymbolVendorELF.h | 58 + .../UnwindAssemblyInstEmulation.cpp | 670 + .../UnwindAssemblyInstEmulation.h | 185 + .../UnwindAssembly/x86/UnwindAssembly-x86.cpp | 973 ++ .../UnwindAssembly/x86/UnwindAssembly-x86.h | 73 + source/Symbol/Block.cpp | 631 + source/Symbol/ClangASTContext.cpp | 2291 +++ source/Symbol/ClangASTImporter.cpp | 718 + source/Symbol/ClangASTType.cpp | 6486 ++++++++ .../ClangExternalASTSourceCallbacks.cpp | 163 + .../Symbol/ClangExternalASTSourceCommon.cpp | 89 + source/Symbol/ClangNamespaceDecl.cpp | 25 + source/Symbol/CompileUnit.cpp | 464 + source/Symbol/DWARFCallFrameInfo.cpp | 815 + source/Symbol/Declaration.cpp | 117 + source/Symbol/FuncUnwinders.cpp | 247 + source/Symbol/Function.cpp | 572 + source/Symbol/LineEntry.cpp | 246 + source/Symbol/LineTable.cpp | 588 + source/Symbol/ObjectFile.cpp | 621 + source/Symbol/Symbol.cpp | 462 + source/Symbol/SymbolContext.cpp | 1205 ++ source/Symbol/SymbolFile.cpp | 89 + source/Symbol/SymbolVendor.cpp | 486 + source/Symbol/Symtab.cpp | 1211 ++ source/Symbol/Type.cpp | 990 ++ source/Symbol/TypeList.cpp | 356 + source/Symbol/UnwindPlan.cpp | 441 + source/Symbol/UnwindTable.cpp | 153 + source/Symbol/Variable.cpp | 894 + source/Symbol/VariableList.cpp | 201 + source/Symbol/VerifyDecl.cpp | 16 + source/Target/ABI.cpp | 175 + source/Target/CPPLanguageRuntime.cpp | 386 + source/Target/ExecutionContext.cpp | 824 + source/Target/LanguageRuntime.cpp | 343 + source/Target/Memory.cpp | 461 + source/Target/ObjCLanguageRuntime.cpp | 605 + source/Target/OperatingSystem.cpp | 67 + source/Target/PathMappingList.cpp | 348 + source/Target/Platform.cpp | 779 + source/Target/Process.cpp | 5595 +++++++ source/Target/RegisterContext.cpp | 600 + source/Target/SectionLoadList.cpp | 276 + source/Target/StackFrame.cpp | 1449 ++ source/Target/StackFrameList.cpp | 899 + source/Target/StackID.cpp | 110 + source/Target/StopInfo.cpp | 1143 ++ source/Target/Target.cpp | 2866 ++++ source/Target/TargetList.cpp | 576 + source/Target/Thread.cpp | 1988 +++ source/Target/ThreadList.cpp | 785 + source/Target/ThreadPlan.cpp | 353 + source/Target/ThreadPlanBase.cpp | 237 + source/Target/ThreadPlanCallFunction.cpp | 614 + .../Target/ThreadPlanCallUserExpression.cpp | 82 + source/Target/ThreadPlanRunToAddress.cpp | 268 + source/Target/ThreadPlanShouldStopHere.cpp | 74 + source/Target/ThreadPlanStepInRange.cpp | 485 + source/Target/ThreadPlanStepInstruction.cpp | 227 + source/Target/ThreadPlanStepOut.cpp | 489 + .../Target/ThreadPlanStepOverBreakpoint.cpp | 165 + source/Target/ThreadPlanStepOverRange.cpp | 388 + source/Target/ThreadPlanStepRange.cpp | 522 + source/Target/ThreadPlanStepThrough.cpp | 290 + source/Target/ThreadPlanStepUntil.cpp | 413 + source/Target/ThreadPlanTracer.cpp | 286 + source/Target/ThreadSpec.cpp | 158 + source/Target/UnixSignals.cpp | 293 + source/Target/UnwindAssembly.cpp | 41 + source/Utility/ARM_DWARF_Registers.cpp | 435 + source/Utility/ARM_DWARF_Registers.h | 217 + source/Utility/ARM_GCC_Registers.h | 146 + source/Utility/KQueue.cpp | 87 + source/Utility/KQueue.h | 72 + source/Utility/PseudoTerminal.cpp | 339 + source/Utility/Range.cpp | 103 + source/Utility/RefCounter.cpp | 25 + source/Utility/SharingPtr.cpp | 158 + source/Utility/StringExtractor.cpp | 397 + source/Utility/StringExtractor.h | 141 + source/Utility/StringExtractorGDBRemote.cpp | 182 + source/Utility/StringExtractorGDBRemote.h | 103 + source/Utility/TimeSpecTimeout.cpp | 48 + source/Utility/TimeSpecTimeout.h | 90 + source/Utility/UuidCompatibility.h | 18 + source/lldb-log.cpp | 257 + source/lldb.cpp | 425 + tools/driver/Driver.cpp | 1733 ++ tools/driver/Driver.h | 201 + tools/driver/IOChannel.cpp | 647 + tools/driver/IOChannel.h | 174 + 885 files changed, 362847 insertions(+) create mode 100644 LICENSE.TXT create mode 100644 include/lldb/API/LLDB.h create mode 100644 include/lldb/API/SBAddress.h create mode 100644 include/lldb/API/SBBlock.h create mode 100644 include/lldb/API/SBBreakpoint.h create mode 100644 include/lldb/API/SBBreakpointLocation.h create mode 100644 include/lldb/API/SBBroadcaster.h create mode 100644 include/lldb/API/SBCommandInterpreter.h create mode 100644 include/lldb/API/SBCommandReturnObject.h create mode 100644 include/lldb/API/SBCommunication.h create mode 100644 include/lldb/API/SBCompileUnit.h create mode 100644 include/lldb/API/SBData.h create mode 100644 include/lldb/API/SBDebugger.h create mode 100644 include/lldb/API/SBDeclaration.h create mode 100644 include/lldb/API/SBDefines.h create mode 100644 include/lldb/API/SBError.h create mode 100644 include/lldb/API/SBEvent.h create mode 100644 include/lldb/API/SBExpressionOptions.h create mode 100644 include/lldb/API/SBFileSpec.h create mode 100644 include/lldb/API/SBFileSpecList.h create mode 100644 include/lldb/API/SBFrame.h create mode 100644 include/lldb/API/SBFunction.h create mode 100644 include/lldb/API/SBHostOS.h create mode 100644 include/lldb/API/SBInputReader.h create mode 100644 include/lldb/API/SBInstruction.h create mode 100644 include/lldb/API/SBInstructionList.h create mode 100644 include/lldb/API/SBLineEntry.h create mode 100644 include/lldb/API/SBListener.h create mode 100644 include/lldb/API/SBModule.h create mode 100644 include/lldb/API/SBModuleSpec.h create mode 100644 include/lldb/API/SBProcess.h create mode 100644 include/lldb/API/SBSection.h create mode 100644 include/lldb/API/SBSourceManager.h create mode 100644 include/lldb/API/SBStream.h create mode 100644 include/lldb/API/SBStringList.h create mode 100644 include/lldb/API/SBSymbol.h create mode 100644 include/lldb/API/SBSymbolContext.h create mode 100644 include/lldb/API/SBSymbolContextList.h create mode 100644 include/lldb/API/SBTarget.h create mode 100644 include/lldb/API/SBThread.h create mode 100644 include/lldb/API/SBType.h create mode 100644 include/lldb/API/SBTypeCategory.h create mode 100644 include/lldb/API/SBTypeFilter.h create mode 100644 include/lldb/API/SBTypeFormat.h create mode 100644 include/lldb/API/SBTypeNameSpecifier.h create mode 100644 include/lldb/API/SBTypeSummary.h create mode 100644 include/lldb/API/SBTypeSynthetic.h create mode 100644 include/lldb/API/SBValue.h create mode 100644 include/lldb/API/SBValueList.h create mode 100644 include/lldb/API/SBWatchpoint.h create mode 100644 include/lldb/Breakpoint/Breakpoint.h create mode 100644 include/lldb/Breakpoint/BreakpointID.h create mode 100644 include/lldb/Breakpoint/BreakpointIDList.h create mode 100644 include/lldb/Breakpoint/BreakpointList.h create mode 100644 include/lldb/Breakpoint/BreakpointLocation.h create mode 100644 include/lldb/Breakpoint/BreakpointLocationCollection.h create mode 100644 include/lldb/Breakpoint/BreakpointLocationList.h create mode 100644 include/lldb/Breakpoint/BreakpointOptions.h create mode 100644 include/lldb/Breakpoint/BreakpointResolver.h create mode 100644 include/lldb/Breakpoint/BreakpointResolverAddress.h create mode 100644 include/lldb/Breakpoint/BreakpointResolverFileLine.h create mode 100644 include/lldb/Breakpoint/BreakpointResolverFileRegex.h create mode 100644 include/lldb/Breakpoint/BreakpointResolverName.h create mode 100644 include/lldb/Breakpoint/BreakpointSite.h create mode 100644 include/lldb/Breakpoint/BreakpointSiteList.h create mode 100644 include/lldb/Breakpoint/Stoppoint.h create mode 100644 include/lldb/Breakpoint/StoppointCallbackContext.h create mode 100644 include/lldb/Breakpoint/StoppointLocation.h create mode 100644 include/lldb/Breakpoint/Watchpoint.h create mode 100644 include/lldb/Breakpoint/WatchpointList.h create mode 100644 include/lldb/Breakpoint/WatchpointOptions.h create mode 100644 include/lldb/Core/Address.h create mode 100644 include/lldb/Core/AddressRange.h create mode 100644 include/lldb/Core/AddressResolver.h create mode 100644 include/lldb/Core/AddressResolverFileLine.h create mode 100644 include/lldb/Core/AddressResolverName.h create mode 100644 include/lldb/Core/ArchSpec.h create mode 100644 include/lldb/Core/Baton.h create mode 100644 include/lldb/Core/Broadcaster.h create mode 100644 include/lldb/Core/ClangForward.h create mode 100644 include/lldb/Core/Communication.h create mode 100644 include/lldb/Core/Connection.h create mode 100644 include/lldb/Core/ConnectionFileDescriptor.h create mode 100644 include/lldb/Core/ConnectionMachPort.h create mode 100644 include/lldb/Core/ConnectionSharedMemory.h create mode 100644 include/lldb/Core/ConstString.h create mode 100644 include/lldb/Core/DataBuffer.h create mode 100644 include/lldb/Core/DataBufferHeap.h create mode 100644 include/lldb/Core/DataBufferMemoryMap.h create mode 100644 include/lldb/Core/DataEncoder.h create mode 100644 include/lldb/Core/DataExtractor.h create mode 100644 include/lldb/Core/Debugger.h create mode 100644 include/lldb/Core/Disassembler.h create mode 100644 include/lldb/Core/EmulateInstruction.h create mode 100644 include/lldb/Core/Error.h create mode 100644 include/lldb/Core/Event.h create mode 100644 include/lldb/Core/FileLineResolver.h create mode 100644 include/lldb/Core/FileSpecList.h create mode 100644 include/lldb/Core/Flags.h create mode 100644 include/lldb/Core/History.h create mode 100644 include/lldb/Core/IOStreamMacros.h create mode 100644 include/lldb/Core/InputReader.h create mode 100644 include/lldb/Core/InputReaderEZ.h create mode 100644 include/lldb/Core/InputReaderStack.h create mode 100644 include/lldb/Core/Language.h create mode 100644 include/lldb/Core/Listener.h create mode 100644 include/lldb/Core/Log.h create mode 100644 include/lldb/Core/Mangled.h create mode 100644 include/lldb/Core/MappedHash.h create mode 100644 include/lldb/Core/Module.h create mode 100644 include/lldb/Core/ModuleChild.h create mode 100644 include/lldb/Core/ModuleList.h create mode 100644 include/lldb/Core/ModuleSpec.h create mode 100644 include/lldb/Core/Opcode.h create mode 100644 include/lldb/Core/PluginInterface.h create mode 100644 include/lldb/Core/PluginManager.h create mode 100644 include/lldb/Core/RangeMap.h create mode 100644 include/lldb/Core/RegisterValue.h create mode 100644 include/lldb/Core/RegularExpression.h create mode 100644 include/lldb/Core/STLUtils.h create mode 100644 include/lldb/Core/Scalar.h create mode 100644 include/lldb/Core/SearchFilter.h create mode 100644 include/lldb/Core/Section.h create mode 100644 include/lldb/Core/SourceManager.h create mode 100644 include/lldb/Core/State.h create mode 100644 include/lldb/Core/Stream.h create mode 100644 include/lldb/Core/StreamAsynchronousIO.h create mode 100644 include/lldb/Core/StreamBuffer.h create mode 100644 include/lldb/Core/StreamCallback.h create mode 100644 include/lldb/Core/StreamFile.h create mode 100644 include/lldb/Core/StreamString.h create mode 100644 include/lldb/Core/StreamTee.h create mode 100644 include/lldb/Core/StringList.h create mode 100644 include/lldb/Core/ThreadSafeSTLMap.h create mode 100644 include/lldb/Core/ThreadSafeValue.h create mode 100644 include/lldb/Core/Timer.h create mode 100644 include/lldb/Core/UUID.h create mode 100644 include/lldb/Core/UniqueCStringMap.h create mode 100644 include/lldb/Core/UserID.h create mode 100644 include/lldb/Core/UserSettingsController.h create mode 100644 include/lldb/Core/VMRange.h create mode 100644 include/lldb/Core/Value.h create mode 100644 include/lldb/Core/ValueObject.h create mode 100644 include/lldb/Core/ValueObjectCast.h create mode 100644 include/lldb/Core/ValueObjectChild.h create mode 100644 include/lldb/Core/ValueObjectConstResult.h create mode 100644 include/lldb/Core/ValueObjectConstResultChild.h create mode 100644 include/lldb/Core/ValueObjectConstResultImpl.h create mode 100644 include/lldb/Core/ValueObjectDynamicValue.h create mode 100644 include/lldb/Core/ValueObjectList.h create mode 100644 include/lldb/Core/ValueObjectMemory.h create mode 100644 include/lldb/Core/ValueObjectRegister.h create mode 100644 include/lldb/Core/ValueObjectSyntheticFilter.h create mode 100644 include/lldb/Core/ValueObjectVariable.h create mode 100644 include/lldb/Core/dwarf.h create mode 100644 include/lldb/DataFormatters/CXXFormatterFunctions.h create mode 100644 include/lldb/DataFormatters/DataVisualization.h create mode 100644 include/lldb/DataFormatters/FormatCache.h create mode 100644 include/lldb/DataFormatters/FormatClasses.h create mode 100644 include/lldb/DataFormatters/FormatManager.h create mode 100644 include/lldb/DataFormatters/FormatNavigator.h create mode 100644 include/lldb/DataFormatters/TypeCategory.h create mode 100644 include/lldb/DataFormatters/TypeCategoryMap.h create mode 100644 include/lldb/DataFormatters/TypeFormat.h create mode 100644 include/lldb/DataFormatters/TypeSummary.h create mode 100644 include/lldb/DataFormatters/TypeSynthetic.h create mode 100644 include/lldb/Expression/ASTDumper.h create mode 100644 include/lldb/Expression/ASTResultSynthesizer.h create mode 100644 include/lldb/Expression/ASTStructExtractor.h create mode 100644 include/lldb/Expression/ClangASTSource.h create mode 100644 include/lldb/Expression/ClangExpression.h create mode 100644 include/lldb/Expression/ClangExpressionDeclMap.h create mode 100644 include/lldb/Expression/ClangExpressionParser.h create mode 100644 include/lldb/Expression/ClangExpressionVariable.h create mode 100644 include/lldb/Expression/ClangFunction.h create mode 100644 include/lldb/Expression/ClangPersistentVariables.h create mode 100644 include/lldb/Expression/ClangUserExpression.h create mode 100644 include/lldb/Expression/ClangUtilityFunction.h create mode 100644 include/lldb/Expression/DWARFExpression.h create mode 100644 include/lldb/Expression/ExpressionSourceCode.h create mode 100644 include/lldb/Expression/IRDynamicChecks.h create mode 100644 include/lldb/Expression/IRExecutionUnit.h create mode 100644 include/lldb/Expression/IRForTarget.h create mode 100644 include/lldb/Expression/IRInterpreter.h create mode 100644 include/lldb/Expression/IRMemoryMap.h create mode 100644 include/lldb/Expression/IRToDWARF.h create mode 100644 include/lldb/Expression/Materializer.h create mode 100644 include/lldb/Host/Condition.h create mode 100644 include/lldb/Host/Config.h create mode 100644 include/lldb/Host/DynamicLibrary.h create mode 100644 include/lldb/Host/Endian.h create mode 100644 include/lldb/Host/File.h create mode 100644 include/lldb/Host/FileSpec.h create mode 100644 include/lldb/Host/Host.h create mode 100644 include/lldb/Host/Mutex.h create mode 100644 include/lldb/Host/Predicate.h create mode 100644 include/lldb/Host/ProcessRunLock.h create mode 100644 include/lldb/Host/SocketAddress.h create mode 100644 include/lldb/Host/Symbols.h create mode 100644 include/lldb/Host/Terminal.h create mode 100644 include/lldb/Host/TimeValue.h create mode 100644 include/lldb/Host/freebsd/Config.h create mode 100644 include/lldb/Interpreter/Args.h create mode 100644 include/lldb/Interpreter/CommandCompletions.h create mode 100644 include/lldb/Interpreter/CommandHistory.h create mode 100644 include/lldb/Interpreter/CommandInterpreter.h create mode 100644 include/lldb/Interpreter/CommandObject.h create mode 100644 include/lldb/Interpreter/CommandObjectMultiword.h create mode 100644 include/lldb/Interpreter/CommandObjectRegexCommand.h create mode 100644 include/lldb/Interpreter/CommandReturnObject.h create mode 100644 include/lldb/Interpreter/OptionGroupArchitecture.h create mode 100644 include/lldb/Interpreter/OptionGroupBoolean.h create mode 100644 include/lldb/Interpreter/OptionGroupFile.h create mode 100644 include/lldb/Interpreter/OptionGroupFormat.h create mode 100644 include/lldb/Interpreter/OptionGroupOutputFile.h create mode 100644 include/lldb/Interpreter/OptionGroupPlatform.h create mode 100644 include/lldb/Interpreter/OptionGroupString.h create mode 100644 include/lldb/Interpreter/OptionGroupUInt64.h create mode 100644 include/lldb/Interpreter/OptionGroupUUID.h create mode 100644 include/lldb/Interpreter/OptionGroupValueObjectDisplay.h create mode 100644 include/lldb/Interpreter/OptionGroupVariable.h create mode 100644 include/lldb/Interpreter/OptionGroupWatchpoint.h create mode 100644 include/lldb/Interpreter/OptionValue.h create mode 100644 include/lldb/Interpreter/OptionValueArch.h create mode 100644 include/lldb/Interpreter/OptionValueArgs.h create mode 100644 include/lldb/Interpreter/OptionValueArray.h create mode 100644 include/lldb/Interpreter/OptionValueBoolean.h create mode 100644 include/lldb/Interpreter/OptionValueDictionary.h create mode 100644 include/lldb/Interpreter/OptionValueEnumeration.h create mode 100644 include/lldb/Interpreter/OptionValueFileSpec.h create mode 100644 include/lldb/Interpreter/OptionValueFileSpecList.h create mode 100644 include/lldb/Interpreter/OptionValueFormat.h create mode 100644 include/lldb/Interpreter/OptionValuePathMappings.h create mode 100644 include/lldb/Interpreter/OptionValueProperties.h create mode 100644 include/lldb/Interpreter/OptionValueRegex.h create mode 100644 include/lldb/Interpreter/OptionValueSInt64.h create mode 100644 include/lldb/Interpreter/OptionValueString.h create mode 100644 include/lldb/Interpreter/OptionValueUInt64.h create mode 100644 include/lldb/Interpreter/OptionValueUUID.h create mode 100644 include/lldb/Interpreter/OptionValues.h create mode 100644 include/lldb/Interpreter/Options.h create mode 100644 include/lldb/Interpreter/Property.h create mode 100644 include/lldb/Interpreter/PythonDataObjects.h create mode 100644 include/lldb/Interpreter/ScriptInterpreter.h create mode 100644 include/lldb/Interpreter/ScriptInterpreterNone.h create mode 100644 include/lldb/Interpreter/ScriptInterpreterPython.h create mode 100644 include/lldb/Symbol/Block.h create mode 100644 include/lldb/Symbol/ClangASTContext.h create mode 100644 include/lldb/Symbol/ClangASTImporter.h create mode 100644 include/lldb/Symbol/ClangASTType.h create mode 100644 include/lldb/Symbol/ClangExternalASTSourceCallbacks.h create mode 100644 include/lldb/Symbol/ClangExternalASTSourceCommon.h create mode 100644 include/lldb/Symbol/ClangNamespaceDecl.h create mode 100644 include/lldb/Symbol/CompileUnit.h create mode 100644 include/lldb/Symbol/DWARFCallFrameInfo.h create mode 100644 include/lldb/Symbol/Declaration.h create mode 100644 include/lldb/Symbol/FuncUnwinders.h create mode 100644 include/lldb/Symbol/Function.h create mode 100644 include/lldb/Symbol/LineEntry.h create mode 100644 include/lldb/Symbol/LineTable.h create mode 100644 include/lldb/Symbol/ObjectContainer.h create mode 100644 include/lldb/Symbol/ObjectFile.h create mode 100644 include/lldb/Symbol/Symbol.h create mode 100644 include/lldb/Symbol/SymbolContext.h create mode 100644 include/lldb/Symbol/SymbolContextScope.h create mode 100644 include/lldb/Symbol/SymbolFile.h create mode 100644 include/lldb/Symbol/SymbolVendor.h create mode 100644 include/lldb/Symbol/Symtab.h create mode 100644 include/lldb/Symbol/TaggedASTType.h create mode 100644 include/lldb/Symbol/Type.h create mode 100644 include/lldb/Symbol/TypeList.h create mode 100644 include/lldb/Symbol/TypeVendor.h create mode 100644 include/lldb/Symbol/UnwindPlan.h create mode 100644 include/lldb/Symbol/UnwindTable.h create mode 100644 include/lldb/Symbol/Variable.h create mode 100644 include/lldb/Symbol/VariableList.h create mode 100644 include/lldb/Symbol/VerifyDecl.h create mode 100644 include/lldb/Target/ABI.h create mode 100644 include/lldb/Target/CPPLanguageRuntime.h create mode 100644 include/lldb/Target/DynamicLoader.h create mode 100644 include/lldb/Target/ExecutionContext.h create mode 100644 include/lldb/Target/ExecutionContextScope.h create mode 100644 include/lldb/Target/LanguageRuntime.h create mode 100644 include/lldb/Target/Memory.h create mode 100644 include/lldb/Target/ObjCLanguageRuntime.h create mode 100644 include/lldb/Target/OperatingSystem.h create mode 100644 include/lldb/Target/PathMappingList.h create mode 100644 include/lldb/Target/Platform.h create mode 100644 include/lldb/Target/Process.h create mode 100644 include/lldb/Target/RegisterContext.h create mode 100644 include/lldb/Target/SectionLoadList.h create mode 100644 include/lldb/Target/StackFrame.h create mode 100644 include/lldb/Target/StackFrameList.h create mode 100644 include/lldb/Target/StackID.h create mode 100644 include/lldb/Target/StopInfo.h create mode 100644 include/lldb/Target/Target.h create mode 100644 include/lldb/Target/TargetList.h create mode 100644 include/lldb/Target/Thread.h create mode 100644 include/lldb/Target/ThreadList.h create mode 100644 include/lldb/Target/ThreadPlan.h create mode 100644 include/lldb/Target/ThreadPlanBase.h create mode 100644 include/lldb/Target/ThreadPlanCallFunction.h create mode 100644 include/lldb/Target/ThreadPlanCallUserExpression.h create mode 100644 include/lldb/Target/ThreadPlanRunToAddress.h create mode 100644 include/lldb/Target/ThreadPlanShouldStopHere.h create mode 100644 include/lldb/Target/ThreadPlanStepInRange.h create mode 100644 include/lldb/Target/ThreadPlanStepInstruction.h create mode 100644 include/lldb/Target/ThreadPlanStepOut.h create mode 100644 include/lldb/Target/ThreadPlanStepOverBreakpoint.h create mode 100644 include/lldb/Target/ThreadPlanStepOverRange.h create mode 100644 include/lldb/Target/ThreadPlanStepRange.h create mode 100644 include/lldb/Target/ThreadPlanStepThrough.h create mode 100644 include/lldb/Target/ThreadPlanStepUntil.h create mode 100644 include/lldb/Target/ThreadPlanTracer.h create mode 100644 include/lldb/Target/ThreadSpec.h create mode 100644 include/lldb/Target/UnixSignals.h create mode 100644 include/lldb/Target/Unwind.h create mode 100644 include/lldb/Target/UnwindAssembly.h create mode 100644 include/lldb/Utility/AnsiTerminal.h create mode 100644 include/lldb/Utility/CleanUp.h create mode 100644 include/lldb/Utility/PriorityPointerPair.h create mode 100644 include/lldb/Utility/PseudoTerminal.h create mode 100644 include/lldb/Utility/PythonPointer.h create mode 100644 include/lldb/Utility/Range.h create mode 100644 include/lldb/Utility/RefCounter.h create mode 100644 include/lldb/Utility/SharedCluster.h create mode 100644 include/lldb/Utility/SharingPtr.h create mode 100644 include/lldb/Utility/Utils.h create mode 100644 include/lldb/lldb-defines.h create mode 100644 include/lldb/lldb-enumerations.h create mode 100644 include/lldb/lldb-forward.h create mode 100644 include/lldb/lldb-private-enumerations.h create mode 100644 include/lldb/lldb-private-interfaces.h create mode 100644 include/lldb/lldb-private-log.h create mode 100644 include/lldb/lldb-private-types.h create mode 100644 include/lldb/lldb-private.h create mode 100644 include/lldb/lldb-public.h create mode 100644 include/lldb/lldb-python.h create mode 100644 include/lldb/lldb-types.h create mode 100644 include/lldb/lldb-versioning.h create mode 100644 source/API/SBAddress.cpp create mode 100644 source/API/SBBlock.cpp create mode 100644 source/API/SBBreakpoint.cpp create mode 100644 source/API/SBBreakpointLocation.cpp create mode 100644 source/API/SBBroadcaster.cpp create mode 100644 source/API/SBCommandInterpreter.cpp create mode 100644 source/API/SBCommandReturnObject.cpp create mode 100644 source/API/SBCommunication.cpp create mode 100644 source/API/SBCompileUnit.cpp create mode 100644 source/API/SBData.cpp create mode 100644 source/API/SBDebugger.cpp create mode 100644 source/API/SBDeclaration.cpp create mode 100644 source/API/SBError.cpp create mode 100644 source/API/SBEvent.cpp create mode 100644 source/API/SBExpressionOptions.cpp create mode 100644 source/API/SBFileSpec.cpp create mode 100644 source/API/SBFileSpecList.cpp create mode 100644 source/API/SBFrame.cpp create mode 100644 source/API/SBFunction.cpp create mode 100644 source/API/SBHostOS.cpp create mode 100644 source/API/SBInputReader.cpp create mode 100644 source/API/SBInstruction.cpp create mode 100644 source/API/SBInstructionList.cpp create mode 100644 source/API/SBLineEntry.cpp create mode 100644 source/API/SBListener.cpp create mode 100644 source/API/SBModule.cpp create mode 100644 source/API/SBModuleSpec.cpp create mode 100644 source/API/SBProcess.cpp create mode 100644 source/API/SBSection.cpp create mode 100644 source/API/SBSourceManager.cpp create mode 100644 source/API/SBStream.cpp create mode 100644 source/API/SBStringList.cpp create mode 100644 source/API/SBSymbol.cpp create mode 100644 source/API/SBSymbolContext.cpp create mode 100644 source/API/SBSymbolContextList.cpp create mode 100644 source/API/SBTarget.cpp create mode 100644 source/API/SBThread.cpp create mode 100644 source/API/SBType.cpp create mode 100644 source/API/SBTypeCategory.cpp create mode 100644 source/API/SBTypeFilter.cpp create mode 100644 source/API/SBTypeFormat.cpp create mode 100644 source/API/SBTypeNameSpecifier.cpp create mode 100644 source/API/SBTypeSummary.cpp create mode 100644 source/API/SBTypeSynthetic.cpp create mode 100644 source/API/SBValue.cpp create mode 100644 source/API/SBValueList.cpp create mode 100644 source/API/SBWatchpoint.cpp create mode 100644 source/Breakpoint/Breakpoint.cpp create mode 100644 source/Breakpoint/BreakpointID.cpp create mode 100644 source/Breakpoint/BreakpointIDList.cpp create mode 100644 source/Breakpoint/BreakpointList.cpp create mode 100644 source/Breakpoint/BreakpointLocation.cpp create mode 100644 source/Breakpoint/BreakpointLocationCollection.cpp create mode 100644 source/Breakpoint/BreakpointLocationList.cpp create mode 100644 source/Breakpoint/BreakpointOptions.cpp create mode 100644 source/Breakpoint/BreakpointResolver.cpp create mode 100644 source/Breakpoint/BreakpointResolverAddress.cpp create mode 100644 source/Breakpoint/BreakpointResolverFileLine.cpp create mode 100644 source/Breakpoint/BreakpointResolverFileRegex.cpp create mode 100644 source/Breakpoint/BreakpointResolverName.cpp create mode 100644 source/Breakpoint/BreakpointSite.cpp create mode 100644 source/Breakpoint/BreakpointSiteList.cpp create mode 100644 source/Breakpoint/Stoppoint.cpp create mode 100644 source/Breakpoint/StoppointCallbackContext.cpp create mode 100644 source/Breakpoint/StoppointLocation.cpp create mode 100644 source/Breakpoint/Watchpoint.cpp create mode 100644 source/Breakpoint/WatchpointList.cpp create mode 100644 source/Breakpoint/WatchpointOptions.cpp create mode 100644 source/Commands/CommandCompletions.cpp create mode 100644 source/Commands/CommandObjectApropos.cpp create mode 100644 source/Commands/CommandObjectApropos.h create mode 100644 source/Commands/CommandObjectArgs.cpp create mode 100644 source/Commands/CommandObjectArgs.h create mode 100644 source/Commands/CommandObjectBreakpoint.cpp create mode 100644 source/Commands/CommandObjectBreakpoint.h create mode 100644 source/Commands/CommandObjectBreakpointCommand.cpp create mode 100644 source/Commands/CommandObjectBreakpointCommand.h create mode 100644 source/Commands/CommandObjectCommands.cpp create mode 100644 source/Commands/CommandObjectCommands.h create mode 100644 source/Commands/CommandObjectDisassemble.cpp create mode 100644 source/Commands/CommandObjectDisassemble.h create mode 100644 source/Commands/CommandObjectExpression.cpp create mode 100644 source/Commands/CommandObjectExpression.h create mode 100644 source/Commands/CommandObjectFrame.cpp create mode 100644 source/Commands/CommandObjectFrame.h create mode 100644 source/Commands/CommandObjectHelp.cpp create mode 100644 source/Commands/CommandObjectHelp.h create mode 100644 source/Commands/CommandObjectLog.cpp create mode 100644 source/Commands/CommandObjectLog.h create mode 100644 source/Commands/CommandObjectMemory.cpp create mode 100644 source/Commands/CommandObjectMemory.h create mode 100644 source/Commands/CommandObjectMultiword.cpp create mode 100644 source/Commands/CommandObjectPlatform.cpp create mode 100644 source/Commands/CommandObjectPlatform.h create mode 100644 source/Commands/CommandObjectPlugin.cpp create mode 100644 source/Commands/CommandObjectPlugin.h create mode 100644 source/Commands/CommandObjectProcess.cpp create mode 100644 source/Commands/CommandObjectProcess.h create mode 100644 source/Commands/CommandObjectQuit.cpp create mode 100644 source/Commands/CommandObjectQuit.h create mode 100644 source/Commands/CommandObjectRegister.cpp create mode 100644 source/Commands/CommandObjectRegister.h create mode 100644 source/Commands/CommandObjectSettings.cpp create mode 100644 source/Commands/CommandObjectSettings.h create mode 100644 source/Commands/CommandObjectSource.cpp create mode 100644 source/Commands/CommandObjectSource.h create mode 100644 source/Commands/CommandObjectSyntax.cpp create mode 100644 source/Commands/CommandObjectSyntax.h create mode 100644 source/Commands/CommandObjectTarget.cpp create mode 100644 source/Commands/CommandObjectTarget.h create mode 100644 source/Commands/CommandObjectThread.cpp create mode 100644 source/Commands/CommandObjectThread.h create mode 100644 source/Commands/CommandObjectType.cpp create mode 100644 source/Commands/CommandObjectType.h create mode 100644 source/Commands/CommandObjectVersion.cpp create mode 100644 source/Commands/CommandObjectVersion.h create mode 100644 source/Commands/CommandObjectWatchpoint.cpp create mode 100644 source/Commands/CommandObjectWatchpoint.h create mode 100644 source/Commands/CommandObjectWatchpointCommand.cpp create mode 100644 source/Commands/CommandObjectWatchpointCommand.h create mode 100644 source/Core/Address.cpp create mode 100644 source/Core/AddressRange.cpp create mode 100644 source/Core/AddressResolver.cpp create mode 100644 source/Core/AddressResolverFileLine.cpp create mode 100644 source/Core/AddressResolverName.cpp create mode 100644 source/Core/ArchSpec.cpp create mode 100644 source/Core/Baton.cpp create mode 100644 source/Core/Broadcaster.cpp create mode 100644 source/Core/Communication.cpp create mode 100644 source/Core/Connection.cpp create mode 100644 source/Core/ConnectionFileDescriptor.cpp create mode 100644 source/Core/ConnectionMachPort.cpp create mode 100644 source/Core/ConnectionSharedMemory.cpp create mode 100644 source/Core/ConstString.cpp create mode 100644 source/Core/DataBufferHeap.cpp create mode 100644 source/Core/DataBufferMemoryMap.cpp create mode 100644 source/Core/DataEncoder.cpp create mode 100644 source/Core/DataExtractor.cpp create mode 100644 source/Core/Debugger.cpp create mode 100644 source/Core/Disassembler.cpp create mode 100644 source/Core/DynamicLoader.cpp create mode 100644 source/Core/EmulateInstruction.cpp create mode 100644 source/Core/Error.cpp create mode 100644 source/Core/Event.cpp create mode 100644 source/Core/FileLineResolver.cpp create mode 100644 source/Core/FileSpecList.cpp create mode 100644 source/Core/History.cpp create mode 100644 source/Core/InputReader.cpp create mode 100644 source/Core/InputReaderEZ.cpp create mode 100644 source/Core/InputReaderStack.cpp create mode 100644 source/Core/Language.cpp create mode 100644 source/Core/Listener.cpp create mode 100644 source/Core/Log.cpp create mode 100644 source/Core/Mangled.cpp create mode 100644 source/Core/Module.cpp create mode 100644 source/Core/ModuleChild.cpp create mode 100644 source/Core/ModuleList.cpp create mode 100644 source/Core/Opcode.cpp create mode 100644 source/Core/PluginManager.cpp create mode 100644 source/Core/RegisterValue.cpp create mode 100644 source/Core/RegularExpression.cpp create mode 100644 source/Core/Scalar.cpp create mode 100644 source/Core/SearchFilter.cpp create mode 100644 source/Core/Section.cpp create mode 100644 source/Core/SourceManager.cpp create mode 100644 source/Core/State.cpp create mode 100644 source/Core/Stream.cpp create mode 100644 source/Core/StreamAsynchronousIO.cpp create mode 100644 source/Core/StreamCallback.cpp create mode 100644 source/Core/StreamFile.cpp create mode 100644 source/Core/StreamString.cpp create mode 100644 source/Core/StringList.cpp create mode 100644 source/Core/Timer.cpp create mode 100644 source/Core/UUID.cpp create mode 100644 source/Core/UserID.cpp create mode 100644 source/Core/UserSettingsController.cpp create mode 100644 source/Core/VMRange.cpp create mode 100644 source/Core/Value.cpp create mode 100644 source/Core/ValueObject.cpp create mode 100644 source/Core/ValueObjectCast.cpp create mode 100644 source/Core/ValueObjectChild.cpp create mode 100644 source/Core/ValueObjectConstResult.cpp create mode 100644 source/Core/ValueObjectConstResultChild.cpp create mode 100644 source/Core/ValueObjectConstResultImpl.cpp create mode 100644 source/Core/ValueObjectDynamicValue.cpp create mode 100644 source/Core/ValueObjectList.cpp create mode 100644 source/Core/ValueObjectMemory.cpp create mode 100644 source/Core/ValueObjectRegister.cpp create mode 100644 source/Core/ValueObjectSyntheticFilter.cpp create mode 100644 source/Core/ValueObjectVariable.cpp create mode 100644 source/DataFormatters/CF.cpp create mode 100644 source/DataFormatters/CXXFormatterFunctions.cpp create mode 100644 source/DataFormatters/Cocoa.cpp create mode 100644 source/DataFormatters/DataVisualization.cpp create mode 100644 source/DataFormatters/FormatCache.cpp create mode 100644 source/DataFormatters/FormatClasses.cpp create mode 100644 source/DataFormatters/FormatManager.cpp create mode 100644 source/DataFormatters/LibCxx.cpp create mode 100644 source/DataFormatters/LibCxxList.cpp create mode 100644 source/DataFormatters/LibCxxMap.cpp create mode 100644 source/DataFormatters/LibStdcpp.cpp create mode 100644 source/DataFormatters/NSArray.cpp create mode 100644 source/DataFormatters/NSDictionary.cpp create mode 100644 source/DataFormatters/NSSet.cpp create mode 100644 source/DataFormatters/TypeCategory.cpp create mode 100644 source/DataFormatters/TypeCategoryMap.cpp create mode 100644 source/DataFormatters/TypeFormat.cpp create mode 100644 source/DataFormatters/TypeSummary.cpp create mode 100644 source/DataFormatters/TypeSynthetic.cpp create mode 100644 source/Expression/ASTDumper.cpp create mode 100644 source/Expression/ASTResultSynthesizer.cpp create mode 100644 source/Expression/ASTStructExtractor.cpp create mode 100644 source/Expression/ClangASTSource.cpp create mode 100644 source/Expression/ClangExpressionDeclMap.cpp create mode 100644 source/Expression/ClangExpressionParser.cpp create mode 100644 source/Expression/ClangExpressionVariable.cpp create mode 100644 source/Expression/ClangFunction.cpp create mode 100644 source/Expression/ClangPersistentVariables.cpp create mode 100644 source/Expression/ClangUserExpression.cpp create mode 100644 source/Expression/ClangUtilityFunction.cpp create mode 100644 source/Expression/DWARFExpression.cpp create mode 100644 source/Expression/ExpressionSourceCode.cpp create mode 100644 source/Expression/IRDynamicChecks.cpp create mode 100644 source/Expression/IRExecutionUnit.cpp create mode 100644 source/Expression/IRForTarget.cpp create mode 100644 source/Expression/IRInterpreter.cpp create mode 100644 source/Expression/IRMemoryMap.cpp create mode 100644 source/Expression/Materializer.cpp create mode 100644 source/Host/common/Condition.cpp create mode 100644 source/Host/common/DynamicLibrary.cpp create mode 100644 source/Host/common/File.cpp create mode 100644 source/Host/common/FileSpec.cpp create mode 100644 source/Host/common/Host.cpp create mode 100644 source/Host/common/Mutex.cpp create mode 100644 source/Host/common/SocketAddress.cpp create mode 100644 source/Host/common/Symbols.cpp create mode 100644 source/Host/common/Terminal.cpp create mode 100644 source/Host/common/TimeValue.cpp create mode 100644 source/Host/freebsd/Host.cpp create mode 100644 source/Interpreter/Args.cpp create mode 100644 source/Interpreter/CommandHistory.cpp create mode 100644 source/Interpreter/CommandInterpreter.cpp create mode 100644 source/Interpreter/CommandObject.cpp create mode 100644 source/Interpreter/CommandObjectRegexCommand.cpp create mode 100644 source/Interpreter/CommandObjectScript.cpp create mode 100644 source/Interpreter/CommandObjectScript.h create mode 100644 source/Interpreter/CommandReturnObject.cpp create mode 100644 source/Interpreter/OptionGroupArchitecture.cpp create mode 100644 source/Interpreter/OptionGroupBoolean.cpp create mode 100644 source/Interpreter/OptionGroupFile.cpp create mode 100644 source/Interpreter/OptionGroupFormat.cpp create mode 100644 source/Interpreter/OptionGroupOutputFile.cpp create mode 100644 source/Interpreter/OptionGroupPlatform.cpp create mode 100644 source/Interpreter/OptionGroupString.cpp create mode 100644 source/Interpreter/OptionGroupUInt64.cpp create mode 100644 source/Interpreter/OptionGroupUUID.cpp create mode 100644 source/Interpreter/OptionGroupValueObjectDisplay.cpp create mode 100644 source/Interpreter/OptionGroupVariable.cpp create mode 100644 source/Interpreter/OptionGroupWatchpoint.cpp create mode 100644 source/Interpreter/OptionValue.cpp create mode 100644 source/Interpreter/OptionValueArch.cpp create mode 100644 source/Interpreter/OptionValueArgs.cpp create mode 100644 source/Interpreter/OptionValueArray.cpp create mode 100644 source/Interpreter/OptionValueBoolean.cpp create mode 100644 source/Interpreter/OptionValueDictionary.cpp create mode 100644 source/Interpreter/OptionValueEnumeration.cpp create mode 100644 source/Interpreter/OptionValueFileSpec.cpp create mode 100644 source/Interpreter/OptionValueFileSpecLIst.cpp create mode 100644 source/Interpreter/OptionValueFormat.cpp create mode 100644 source/Interpreter/OptionValuePathMappings.cpp create mode 100644 source/Interpreter/OptionValueProperties.cpp create mode 100644 source/Interpreter/OptionValueRegex.cpp create mode 100644 source/Interpreter/OptionValueSInt64.cpp create mode 100644 source/Interpreter/OptionValueString.cpp create mode 100644 source/Interpreter/OptionValueUInt64.cpp create mode 100644 source/Interpreter/OptionValueUUID.cpp create mode 100644 source/Interpreter/Options.cpp create mode 100644 source/Interpreter/Property.cpp create mode 100644 source/Interpreter/PythonDataObjects.cpp create mode 100644 source/Interpreter/ScriptInterpreter.cpp create mode 100644 source/Interpreter/ScriptInterpreterNone.cpp create mode 100644 source/Interpreter/ScriptInterpreterPython.cpp create mode 100644 source/Interpreter/embedded_interpreter.py create mode 100644 source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp create mode 100644 source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h create mode 100644 source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp create mode 100644 source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h create mode 100644 source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp create mode 100644 source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h create mode 100644 source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp create mode 100644 source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp create mode 100644 source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h create mode 100644 source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp create mode 100644 source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h create mode 100644 source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp create mode 100644 source/Plugins/Instruction/ARM/EmulateInstructionARM.h create mode 100644 source/Plugins/Instruction/ARM/EmulationStateARM.cpp create mode 100644 source/Plugins/Instruction/ARM/EmulationStateARM.h create mode 100644 source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.cpp create mode 100644 source/Plugins/LanguageRuntime/CPlusPlus/ItaniumABI/ItaniumABILanguageRuntime.h create mode 100644 source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp create mode 100644 source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.h create mode 100644 source/Plugins/ObjectFile/ELF/ELFHeader.cpp create mode 100644 source/Plugins/ObjectFile/ELF/ELFHeader.h create mode 100644 source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp create mode 100644 source/Plugins/ObjectFile/ELF/ObjectFileELF.h create mode 100644 source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp create mode 100644 source/Plugins/OperatingSystem/Python/OperatingSystemPython.h create mode 100644 source/Plugins/Platform/FreeBSD/PlatformFreeBSD.cpp create mode 100644 source/Plugins/Platform/FreeBSD/PlatformFreeBSD.h create mode 100644 source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp create mode 100644 source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h create mode 100644 source/Plugins/Process/FreeBSD/ProcessFreeBSD.cpp create mode 100644 source/Plugins/Process/FreeBSD/ProcessFreeBSD.h create mode 100644 source/Plugins/Process/FreeBSD/ProcessMonitor.cpp create mode 100644 source/Plugins/Process/FreeBSD/ProcessMonitor.h create mode 100644 source/Plugins/Process/POSIX/POSIXStopInfo.cpp create mode 100644 source/Plugins/Process/POSIX/POSIXStopInfo.h create mode 100644 source/Plugins/Process/POSIX/POSIXThread.cpp create mode 100644 source/Plugins/Process/POSIX/POSIXThread.h create mode 100644 source/Plugins/Process/POSIX/ProcessMessage.cpp create mode 100644 source/Plugins/Process/POSIX/ProcessMessage.h create mode 100644 source/Plugins/Process/POSIX/ProcessPOSIX.cpp create mode 100644 source/Plugins/Process/POSIX/ProcessPOSIX.h create mode 100644 source/Plugins/Process/POSIX/ProcessPOSIXLog.cpp create mode 100644 source/Plugins/Process/POSIX/ProcessPOSIXLog.h create mode 100644 source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp create mode 100644 source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h create mode 100644 source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp create mode 100644 source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h create mode 100644 source/Plugins/Process/POSIX/RegisterContextPOSIX.h create mode 100644 source/Plugins/Process/POSIX/RegisterContext_i386.cpp create mode 100644 source/Plugins/Process/POSIX/RegisterContext_i386.h create mode 100644 source/Plugins/Process/POSIX/RegisterContext_x86.h create mode 100644 source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp create mode 100644 source/Plugins/Process/POSIX/RegisterContext_x86_64.h create mode 100644 source/Plugins/Process/Utility/ARMDefines.h create mode 100644 source/Plugins/Process/Utility/ARMUtils.h create mode 100644 source/Plugins/Process/Utility/DynamicRegisterInfo.cpp create mode 100644 source/Plugins/Process/Utility/DynamicRegisterInfo.h create mode 100644 source/Plugins/Process/Utility/InferiorCallPOSIX.cpp create mode 100644 source/Plugins/Process/Utility/InferiorCallPOSIX.h create mode 100644 source/Plugins/Process/Utility/InstructionUtils.h create mode 100644 source/Plugins/Process/Utility/RegisterContextDarwin_arm.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextDarwin_arm.h create mode 100644 source/Plugins/Process/Utility/RegisterContextDarwin_i386.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextDarwin_i386.h create mode 100644 source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextDarwin_x86_64.h create mode 100644 source/Plugins/Process/Utility/RegisterContextDummy.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextDummy.h create mode 100644 source/Plugins/Process/Utility/RegisterContextLLDB.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextLLDB.h create mode 100644 source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextMacOSXFrameBackchain.h create mode 100644 source/Plugins/Process/Utility/RegisterContextMach_arm.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextMach_arm.h create mode 100644 source/Plugins/Process/Utility/RegisterContextMach_i386.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextMach_i386.h create mode 100644 source/Plugins/Process/Utility/RegisterContextMach_x86_64.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextMach_x86_64.h create mode 100644 source/Plugins/Process/Utility/RegisterContextMemory.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextMemory.h create mode 100644 source/Plugins/Process/Utility/RegisterContextThreadMemory.cpp create mode 100644 source/Plugins/Process/Utility/RegisterContextThreadMemory.h create mode 100644 source/Plugins/Process/Utility/StopInfoMachException.cpp create mode 100644 source/Plugins/Process/Utility/StopInfoMachException.h create mode 100644 source/Plugins/Process/Utility/ThreadMemory.cpp create mode 100644 source/Plugins/Process/Utility/ThreadMemory.h create mode 100644 source/Plugins/Process/Utility/UnwindLLDB.cpp create mode 100644 source/Plugins/Process/Utility/UnwindLLDB.h create mode 100644 source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.cpp create mode 100644 source/Plugins/Process/Utility/UnwindMacOSXFrameBackchain.h create mode 100644 source/Plugins/Process/elf-core/ProcessElfCore.cpp create mode 100644 source/Plugins/Process/elf-core/ProcessElfCore.h create mode 100644 source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.cpp create mode 100644 source/Plugins/Process/elf-core/RegisterContextCoreFreeBSD_x86_64.h create mode 100644 source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.cpp create mode 100644 source/Plugins/Process/elf-core/RegisterContextCoreLinux_x86_64.h create mode 100644 source/Plugins/Process/elf-core/ThreadElfCore.cpp create mode 100644 source/Plugins/Process/elf-core/ThreadElfCore.h create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunication.cpp create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunication.h create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.cpp create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServer.h create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.cpp create mode 100644 source/Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h create mode 100644 source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp create mode 100644 source/Plugins/Process/gdb-remote/ProcessGDBRemote.h create mode 100644 source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.cpp create mode 100644 source/Plugins/Process/gdb-remote/ProcessGDBRemoteLog.h create mode 100644 source/Plugins/Process/gdb-remote/ThreadGDBRemote.cpp create mode 100644 source/Plugins/Process/gdb-remote/ThreadGDBRemote.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFAbbreviationDeclaration.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFAttribute.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFCompileUnit.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDIECollection.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDIECollection.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugAbbrev.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugArangeSet.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugAranges.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugInfo.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugInfoEntry.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugLine.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugLine.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfo.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugMacinfoEntry.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugPubnames.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugPubnamesSet.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDebugRanges.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDeclContext.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDeclContext.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDefines.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFDefines.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFFormValue.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFFormValue.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFLocationDescription.h create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFLocationList.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/DWARFLocationList.h create mode 100644 source/Plugins/SymbolFile/DWARF/HashedNameToDIE.h create mode 100644 source/Plugins/SymbolFile/DWARF/LogChannelDWARF.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/LogChannelDWARF.h create mode 100644 source/Plugins/SymbolFile/DWARF/NameToDIE.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/NameToDIE.h create mode 100644 source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h create mode 100644 source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h create mode 100644 source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.cpp create mode 100644 source/Plugins/SymbolFile/DWARF/UniqueDWARFASTType.h create mode 100644 source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.cpp create mode 100644 source/Plugins/SymbolFile/Symtab/SymbolFileSymtab.h create mode 100644 source/Plugins/SymbolVendor/ELF/SymbolVendorELF.cpp create mode 100644 source/Plugins/SymbolVendor/ELF/SymbolVendorELF.h create mode 100644 source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.cpp create mode 100644 source/Plugins/UnwindAssembly/InstEmulation/UnwindAssemblyInstEmulation.h create mode 100644 source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.cpp create mode 100644 source/Plugins/UnwindAssembly/x86/UnwindAssembly-x86.h create mode 100644 source/Symbol/Block.cpp create mode 100644 source/Symbol/ClangASTContext.cpp create mode 100644 source/Symbol/ClangASTImporter.cpp create mode 100644 source/Symbol/ClangASTType.cpp create mode 100644 source/Symbol/ClangExternalASTSourceCallbacks.cpp create mode 100644 source/Symbol/ClangExternalASTSourceCommon.cpp create mode 100644 source/Symbol/ClangNamespaceDecl.cpp create mode 100644 source/Symbol/CompileUnit.cpp create mode 100644 source/Symbol/DWARFCallFrameInfo.cpp create mode 100644 source/Symbol/Declaration.cpp create mode 100644 source/Symbol/FuncUnwinders.cpp create mode 100644 source/Symbol/Function.cpp create mode 100644 source/Symbol/LineEntry.cpp create mode 100644 source/Symbol/LineTable.cpp create mode 100644 source/Symbol/ObjectFile.cpp create mode 100644 source/Symbol/Symbol.cpp create mode 100644 source/Symbol/SymbolContext.cpp create mode 100644 source/Symbol/SymbolFile.cpp create mode 100644 source/Symbol/SymbolVendor.cpp create mode 100644 source/Symbol/Symtab.cpp create mode 100644 source/Symbol/Type.cpp create mode 100644 source/Symbol/TypeList.cpp create mode 100644 source/Symbol/UnwindPlan.cpp create mode 100644 source/Symbol/UnwindTable.cpp create mode 100644 source/Symbol/Variable.cpp create mode 100644 source/Symbol/VariableList.cpp create mode 100644 source/Symbol/VerifyDecl.cpp create mode 100644 source/Target/ABI.cpp create mode 100644 source/Target/CPPLanguageRuntime.cpp create mode 100644 source/Target/ExecutionContext.cpp create mode 100644 source/Target/LanguageRuntime.cpp create mode 100644 source/Target/Memory.cpp create mode 100644 source/Target/ObjCLanguageRuntime.cpp create mode 100644 source/Target/OperatingSystem.cpp create mode 100644 source/Target/PathMappingList.cpp create mode 100644 source/Target/Platform.cpp create mode 100644 source/Target/Process.cpp create mode 100644 source/Target/RegisterContext.cpp create mode 100644 source/Target/SectionLoadList.cpp create mode 100644 source/Target/StackFrame.cpp create mode 100644 source/Target/StackFrameList.cpp create mode 100644 source/Target/StackID.cpp create mode 100644 source/Target/StopInfo.cpp create mode 100644 source/Target/Target.cpp create mode 100644 source/Target/TargetList.cpp create mode 100644 source/Target/Thread.cpp create mode 100644 source/Target/ThreadList.cpp create mode 100644 source/Target/ThreadPlan.cpp create mode 100644 source/Target/ThreadPlanBase.cpp create mode 100644 source/Target/ThreadPlanCallFunction.cpp create mode 100644 source/Target/ThreadPlanCallUserExpression.cpp create mode 100644 source/Target/ThreadPlanRunToAddress.cpp create mode 100644 source/Target/ThreadPlanShouldStopHere.cpp create mode 100644 source/Target/ThreadPlanStepInRange.cpp create mode 100644 source/Target/ThreadPlanStepInstruction.cpp create mode 100644 source/Target/ThreadPlanStepOut.cpp create mode 100644 source/Target/ThreadPlanStepOverBreakpoint.cpp create mode 100644 source/Target/ThreadPlanStepOverRange.cpp create mode 100644 source/Target/ThreadPlanStepRange.cpp create mode 100644 source/Target/ThreadPlanStepThrough.cpp create mode 100644 source/Target/ThreadPlanStepUntil.cpp create mode 100644 source/Target/ThreadPlanTracer.cpp create mode 100644 source/Target/ThreadSpec.cpp create mode 100644 source/Target/UnixSignals.cpp create mode 100644 source/Target/UnwindAssembly.cpp create mode 100644 source/Utility/ARM_DWARF_Registers.cpp create mode 100644 source/Utility/ARM_DWARF_Registers.h create mode 100644 source/Utility/ARM_GCC_Registers.h create mode 100644 source/Utility/KQueue.cpp create mode 100644 source/Utility/KQueue.h create mode 100644 source/Utility/PseudoTerminal.cpp create mode 100644 source/Utility/Range.cpp create mode 100644 source/Utility/RefCounter.cpp create mode 100644 source/Utility/SharingPtr.cpp create mode 100644 source/Utility/StringExtractor.cpp create mode 100644 source/Utility/StringExtractor.h create mode 100644 source/Utility/StringExtractorGDBRemote.cpp create mode 100644 source/Utility/StringExtractorGDBRemote.h create mode 100644 source/Utility/TimeSpecTimeout.cpp create mode 100644 source/Utility/TimeSpecTimeout.h create mode 100644 source/Utility/UuidCompatibility.h create mode 100644 source/lldb-log.cpp create mode 100644 source/lldb.cpp create mode 100644 tools/driver/Driver.cpp create mode 100644 tools/driver/Driver.h create mode 100644 tools/driver/IOChannel.cpp create mode 100644 tools/driver/IOChannel.h diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 00000000000..1f0309419d7 --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,38 @@ +University of Illinois/NCSA +Open Source License + +Copyright (c) 2010 Apple Inc. +All rights reserved. + +Developed by: + + LLDB Team + + http://lldb.llvm.org/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLDB Team, copyright holders, nor the names of + its contributors may be used to endorse or promote products derived from + this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + diff --git a/include/lldb/API/LLDB.h b/include/lldb/API/LLDB.h new file mode 100644 index 00000000000..93bc3bc121e --- /dev/null +++ b/include/lldb/API/LLDB.h @@ -0,0 +1,54 @@ +//===-- LLDB.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_LLDB_h_ +#define LLDB_LLDB_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommunication.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBData.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBInputReader.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBSymbol.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBValueList.h" + +#endif // LLDB_LLDB_h_ diff --git a/include/lldb/API/SBAddress.h b/include/lldb/API/SBAddress.h new file mode 100644 index 00000000000..c5e8cc685a4 --- /dev/null +++ b/include/lldb/API/SBAddress.h @@ -0,0 +1,150 @@ +//===-- SBAddress.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBAddress_h_ +#define LLDB_SBAddress_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBModule.h" + +namespace lldb { + +class SBAddress +{ +public: + + SBAddress (); + + SBAddress (const lldb::SBAddress &rhs); + + SBAddress (lldb::SBSection section, lldb::addr_t offset); + + // Create an address by resolving a load address using the supplied target + SBAddress (lldb::addr_t load_addr, lldb::SBTarget &target); + + ~SBAddress (); + + const lldb::SBAddress & + operator = (const lldb::SBAddress &rhs); + + bool + IsValid () const; + + void + Clear (); + + addr_t + GetFileAddress () const; + + addr_t + GetLoadAddress (const lldb::SBTarget &target) const; + + void + SetAddress (lldb::SBSection section, lldb::addr_t offset); + + void + SetLoadAddress (lldb::addr_t load_addr, + lldb::SBTarget &target); + bool + OffsetAddress (addr_t offset); + + bool + GetDescription (lldb::SBStream &description); + + // The following queries can lookup symbol information for a given address. + // An address might refer to code or data from an existing module, or it + // might refer to something on the stack or heap. The following functions + // will only return valid values if the address has been resolved to a code + // or data address using "void SBAddress::SetLoadAddress(...)" or + // "lldb::SBAddress SBTarget::ResolveLoadAddress (...)". + lldb::SBSymbolContext + GetSymbolContext (uint32_t resolve_scope); + + + // The following functions grab individual objects for a given address and + // are less efficient if you want more than one symbol related objects. + // Use one of the following when you want multiple debug symbol related + // objects for an address: + // lldb::SBSymbolContext SBAddress::GetSymbolContext (uint32_t resolve_scope); + // lldb::SBSymbolContext SBTarget::ResolveSymbolContextForAddress (const SBAddress &addr, uint32_t resolve_scope); + // One or more bits from the SymbolContextItem enumerations can be logically + // OR'ed together to more efficiently retrieve multiple symbol objects. + + lldb::SBSection + GetSection (); + + lldb::addr_t + GetOffset (); + + lldb::SBModule + GetModule (); + + lldb::SBCompileUnit + GetCompileUnit (); + + lldb::SBFunction + GetFunction (); + + lldb::SBBlock + GetBlock (); + + lldb::SBSymbol + GetSymbol (); + + lldb::SBLineEntry + GetLineEntry (); + + lldb::AddressClass + GetAddressClass (); + +protected: + + friend class SBBlock; + friend class SBBreakpointLocation; + friend class SBFrame; + friend class SBFunction; + friend class SBLineEntry; + friend class SBInstruction; + friend class SBModule; + friend class SBSection; + friend class SBSymbol; + friend class SBSymbolContext; + friend class SBTarget; + friend class SBThread; + friend class SBValue; + + lldb_private::Address * + operator->(); + + const lldb_private::Address * + operator->() const; + + lldb_private::Address * + get (); + + lldb_private::Address & + ref(); + + const lldb_private::Address & + ref() const; + + SBAddress (const lldb_private::Address *lldb_object_ptr); + + void + SetAddress (const lldb_private::Address *lldb_object_ptr); + +private: + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBAddress_h_ diff --git a/include/lldb/API/SBBlock.h b/include/lldb/API/SBBlock.h new file mode 100644 index 00000000000..b8e61fc6eb2 --- /dev/null +++ b/include/lldb/API/SBBlock.h @@ -0,0 +1,123 @@ +//===-- SBBlock.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBlock_h_ +#define LLDB_SBBlock_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBValueList.h" + +namespace lldb { + +class SBBlock +{ +public: + + SBBlock (); + + SBBlock (const lldb::SBBlock &rhs); + + ~SBBlock (); + + const lldb::SBBlock & + operator = (const lldb::SBBlock &rhs); + + bool + IsInlined () const; + + bool + IsValid () const; + + const char * + GetInlinedName () const; + + lldb::SBFileSpec + GetInlinedCallSiteFile () const; + + uint32_t + GetInlinedCallSiteLine () const; + + uint32_t + GetInlinedCallSiteColumn () const; + + lldb::SBBlock + GetParent (); + + lldb::SBBlock + GetSibling (); + + lldb::SBBlock + GetFirstChild (); + + uint32_t + GetNumRanges (); + + lldb::SBAddress + GetRangeStartAddress (uint32_t idx); + + lldb::SBAddress + GetRangeEndAddress (uint32_t idx); + + uint32_t + GetRangeIndexForBlockAddress (lldb::SBAddress block_addr); + + lldb::SBValueList + GetVariables (lldb::SBFrame& frame, + bool arguments, + bool locals, + bool statics, + lldb::DynamicValueType use_dynamic); + + lldb::SBValueList + GetVariables (lldb::SBTarget& target, + bool arguments, + bool locals, + bool statics); + //------------------------------------------------------------------ + /// Get the inlined block that contains this block. + /// + /// @return + /// If this block is inlined, it will return this block, else + /// parent blocks will be searched to see if any contain this + /// block and are themselves inlined. An invalid SBBlock will + /// be returned if this block nor any parent blocks are inlined + /// function blocks. + //------------------------------------------------------------------ + lldb::SBBlock + GetContainingInlinedBlock (); + + bool + GetDescription (lldb::SBStream &description); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBFunction; + friend class SBSymbolContext; + + lldb_private::Block * + GetPtr (); + + void + SetPtr (lldb_private::Block *lldb_object_ptr); + + SBBlock (lldb_private::Block *lldb_object_ptr); + + void + AppendVariables (bool can_create, bool get_parent_variables, lldb_private::VariableList *var_list); + + lldb_private::Block *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBBlock_h_ diff --git a/include/lldb/API/SBBreakpoint.h b/include/lldb/API/SBBreakpoint.h new file mode 100644 index 00000000000..be9c499798e --- /dev/null +++ b/include/lldb/API/SBBreakpoint.h @@ -0,0 +1,175 @@ +//===-- SBBreakpoint.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBreakpoint_h_ +#define LLDB_SBBreakpoint_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBBreakpoint +{ +public: + + typedef bool (*BreakpointHitCallback) (void *baton, + SBProcess &process, + SBThread &thread, + lldb::SBBreakpointLocation &location); + + SBBreakpoint (); + + SBBreakpoint (const lldb::SBBreakpoint& rhs); + + ~SBBreakpoint(); + + const lldb::SBBreakpoint & + operator = (const lldb::SBBreakpoint& rhs); + + // Tests to see if the opaque breakpoint object in this object matches the + // opaque breakpoint object in "rhs". + bool + operator == (const lldb::SBBreakpoint& rhs); + + bool + operator != (const lldb::SBBreakpoint& rhs); + + break_id_t + GetID () const; + + bool + IsValid() const; + + void + ClearAllBreakpointSites (); + + lldb::SBBreakpointLocation + FindLocationByAddress (lldb::addr_t vm_addr); + + lldb::break_id_t + FindLocationIDByAddress (lldb::addr_t vm_addr); + + lldb::SBBreakpointLocation + FindLocationByID (lldb::break_id_t bp_loc_id); + + lldb::SBBreakpointLocation + GetLocationAtIndex (uint32_t index); + + void + SetEnabled (bool enable); + + bool + IsEnabled (); + + void + SetOneShot (bool one_shot); + + bool + IsOneShot () const; + + bool + IsInternal (); + + uint32_t + GetHitCount () const; + + void + SetIgnoreCount (uint32_t count); + + uint32_t + GetIgnoreCount () const; + + void + SetCondition (const char *condition); + + const char * + GetCondition (); + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + void + SetCallback (BreakpointHitCallback callback, void *baton); + + size_t + GetNumResolvedLocations() const; + + size_t + GetNumLocations() const; + + bool + GetDescription (lldb::SBStream &description); + + static bool + EventIsBreakpointEvent (const lldb::SBEvent &event); + + static lldb::BreakpointEventType + GetBreakpointEventTypeFromEvent (const lldb::SBEvent& event); + + static lldb::SBBreakpoint + GetBreakpointFromEvent (const lldb::SBEvent& event); + + static lldb::SBBreakpointLocation + GetBreakpointLocationAtIndexFromEvent (const lldb::SBEvent& event, uint32_t loc_idx); + + static uint32_t + GetNumBreakpointLocationsFromEvent (const lldb::SBEvent &event_sp); + + +private: + friend class SBBreakpointLocation; + friend class SBTarget; + + SBBreakpoint (const lldb::BreakpointSP &bp_sp); + + lldb_private::Breakpoint * + operator->() const; + + lldb_private::Breakpoint * + get() const; + + lldb::BreakpointSP & + operator *(); + + const lldb::BreakpointSP & + operator *() const; + + static bool + PrivateBreakpointHitCallback (void *baton, + lldb_private::StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + lldb::BreakpointSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBBreakpoint_h_ diff --git a/include/lldb/API/SBBreakpointLocation.h b/include/lldb/API/SBBreakpointLocation.h new file mode 100644 index 00000000000..3b2ca2cf88e --- /dev/null +++ b/include/lldb/API/SBBreakpointLocation.h @@ -0,0 +1,110 @@ +//===-- SBBreakpointLocation.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBreakpointLocation_h_ +#define LLDB_SBBreakpointLocation_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBBreakpoint.h" + +namespace lldb { + +class SBBreakpointLocation +{ +public: + + SBBreakpointLocation (); + + SBBreakpointLocation (const lldb::SBBreakpointLocation &rhs); + + ~SBBreakpointLocation (); + + const lldb::SBBreakpointLocation & + operator = (const lldb::SBBreakpointLocation &rhs); + + break_id_t + GetID (); + + bool + IsValid() const; + + lldb::SBAddress + GetAddress (); + + lldb::addr_t + GetLoadAddress (); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + uint32_t + GetIgnoreCount (); + + void + SetIgnoreCount (uint32_t n); + + void + SetCondition (const char *condition); + + const char * + GetCondition (); + + void + SetThreadID (lldb::tid_t sb_thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + bool + IsResolved (); + + bool + GetDescription (lldb::SBStream &description, DescriptionLevel level); + + SBBreakpoint + GetBreakpoint (); + + SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp); + +private: + friend class SBBreakpoint; +#ifndef LLDB_DISABLE_PYTHON + friend class lldb_private::ScriptInterpreterPython; +#endif + void + SetLocation (const lldb::BreakpointLocationSP &break_loc_sp); + + lldb::BreakpointLocationSP m_opaque_sp; + +}; + +} // namespace lldb + +#endif // LLDB_SBBreakpointLocation_h_ diff --git a/include/lldb/API/SBBroadcaster.h b/include/lldb/API/SBBroadcaster.h new file mode 100644 index 00000000000..7b32d85faa0 --- /dev/null +++ b/include/lldb/API/SBBroadcaster.h @@ -0,0 +1,97 @@ +//===-- SBBroadcaster.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBBroadcaster_h_ +#define LLDB_SBBroadcaster_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBBroadcaster +{ +public: + SBBroadcaster (); + + SBBroadcaster (const char *name); + + SBBroadcaster (const SBBroadcaster &rhs); + + const SBBroadcaster & + operator = (const SBBroadcaster &rhs); + + ~SBBroadcaster(); + + bool + IsValid () const; + + void + Clear (); + + void + BroadcastEventByType (uint32_t event_type, bool unique = false); + + void + BroadcastEvent (const lldb::SBEvent &event, bool unique = false); + + void + AddInitialEventsToListener (const lldb::SBListener &listener, uint32_t requested_events); + + uint32_t + AddListener (const lldb::SBListener &listener, uint32_t event_mask); + + const char * + GetName () const; + + bool + EventTypeHasListeners (uint32_t event_type); + + bool + RemoveListener (const lldb::SBListener &listener, uint32_t event_mask = UINT32_MAX); + + // This comparison is checking if the internal opaque pointer value + // is equal to that in "rhs". + bool + operator == (const lldb::SBBroadcaster &rhs) const; + + // This comparison is checking if the internal opaque pointer value + // is not equal to that in "rhs". + bool + operator != (const lldb::SBBroadcaster &rhs) const; + + // This comparison is checking if the internal opaque pointer value + // is less than that in "rhs" so SBBroadcaster objects can be contained + // in ordered containers. + bool + operator < (const lldb::SBBroadcaster &rhs) const; + +protected: + friend class SBCommandInterpreter; + friend class SBCommunication; + friend class SBEvent; + friend class SBListener; + friend class SBProcess; + friend class SBTarget; + + SBBroadcaster (lldb_private::Broadcaster *broadcaster, bool owns); + + lldb_private::Broadcaster * + get () const; + + void + reset (lldb_private::Broadcaster *broadcaster, bool owns); + +private: + lldb::BroadcasterSP m_opaque_sp; + lldb_private::Broadcaster *m_opaque_ptr; +}; + +} // namespace lldb + +#endif // LLDB_SBBroadcaster_h_ diff --git a/include/lldb/API/SBCommandInterpreter.h b/include/lldb/API/SBCommandInterpreter.h new file mode 100644 index 00000000000..9b2583cd85c --- /dev/null +++ b/include/lldb/API/SBCommandInterpreter.h @@ -0,0 +1,193 @@ +//===-- SBCommandInterpreter.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCommandInterpreter_h_ +#define LLDB_SBCommandInterpreter_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBDebugger.h" + +namespace lldb { + +class SBCommandInterpreter +{ +public: + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit + eBroadcastBitAsynchronousOutputData = (1 << 3), + eBroadcastBitAsynchronousErrorData = (1 << 4) + }; + + SBCommandInterpreter (const lldb::SBCommandInterpreter &rhs); + + const lldb::SBCommandInterpreter & + operator = (const lldb::SBCommandInterpreter &rhs); + + ~SBCommandInterpreter (); + + static const char * + GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type); + + static const char * + GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type); + + bool + IsValid() const; + + bool + CommandExists (const char *cmd); + + bool + AliasExists (const char *cmd); + + lldb::SBBroadcaster + GetBroadcaster (); + + static const char * + GetBroadcasterClass (); + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasAliasOptions (); + + lldb::SBProcess + GetProcess (); + + lldb::SBDebugger + GetDebugger (); + + lldb::SBCommand + AddMultiwordCommand (const char* name, const char* help); + + lldb::SBCommand + AddCommand (const char* name, lldb::SBCommandPluginInterface *impl, const char* help); + + void + SourceInitFileInHomeDirectory (lldb::SBCommandReturnObject &result); + + void + SourceInitFileInCurrentWorkingDirectory (lldb::SBCommandReturnObject &result); + + lldb::ReturnStatus + HandleCommand (const char *command_line, lldb::SBCommandReturnObject &result, bool add_to_history = false); + + // The pointer based interface is not useful in SWIG, since the cursor & last_char arguments are string pointers INTO current_line + // and you can't do that in a scripting language interface in general... + + // In either case, the way this works is that the you give it a line and cursor position in the line. The function + // will return the number of completions. The matches list will contain number_of_completions + 1 elements. The first + // element is the common substring after the cursor position for all the matches. The rest of the elements are the + // matches. The first element is useful if you are emulating the common shell behavior where the tab completes + // to the string that is common among all the matches, then you should first check if the first element is non-empty, + // and if so just insert it and move the cursor to the end of the insertion. The next tab will return an empty + // common substring, and a list of choices (if any), at which point you should display the choices and let the user + // type further to disambiguate. + + int + HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + lldb::SBStringList &matches); + + int + HandleCompletion (const char *current_line, + uint32_t cursor_pos, + int match_start_point, + int max_return_elements, + lldb::SBStringList &matches); + + // Catch commands before they execute by registering a callback that will + // get called when the command gets executed. This allows GUI or command + // line interfaces to intercept a command and stop it from happening + bool + SetCommandOverrideCallback (const char *command_name, + lldb::CommandOverrideCallback callback, + void *baton); + + SBCommandInterpreter (lldb_private::CommandInterpreter *interpreter_ptr = NULL); // Access using SBDebugger::GetCommandInterpreter(); + +protected: + + lldb_private::CommandInterpreter & + ref (); + + lldb_private::CommandInterpreter * + get (); + + void + reset (lldb_private::CommandInterpreter *); +private: + friend class SBDebugger; + + static void + InitializeSWIG (); + + lldb_private::CommandInterpreter *m_opaque_ptr; +}; + +class SBCommandPluginInterface +{ +public: + virtual bool + DoExecute (lldb::SBDebugger debugger, + char** command, + lldb::SBCommandReturnObject &result) + { + return false; + } + + virtual + ~SBCommandPluginInterface () + {} +}; + +class SBCommand +{ +public: + + SBCommand (); + + bool + IsValid (); + + const char* + GetName (); + + const char* + GetHelp (); + + lldb::SBCommand + AddMultiwordCommand (const char* name, const char* help = NULL); + + lldb::SBCommand + AddCommand (const char* name, lldb::SBCommandPluginInterface* impl, const char* help = NULL); + +private: + + friend class SBDebugger; + friend class SBCommandInterpreter; + + SBCommand (lldb::CommandObjectSP cmd_sp); + + lldb::CommandObjectSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBCommandInterpreter_h_ diff --git a/include/lldb/API/SBCommandReturnObject.h b/include/lldb/API/SBCommandReturnObject.h new file mode 100644 index 00000000000..f2d27480233 --- /dev/null +++ b/include/lldb/API/SBCommandReturnObject.h @@ -0,0 +1,133 @@ +//===-- SBCommandReturnObject.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCommandReturnObject_h_ +#define LLDB_SBCommandReturnObject_h_ + +#include + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBCommandReturnObject +{ +public: + + SBCommandReturnObject (); + + SBCommandReturnObject (const lldb::SBCommandReturnObject &rhs); + + const lldb::SBCommandReturnObject & + operator = (const lldb::SBCommandReturnObject &rhs); + + + SBCommandReturnObject (lldb_private::CommandReturnObject *ptr); + + lldb_private::CommandReturnObject * + Release (); + + ~SBCommandReturnObject (); + + bool + IsValid() const; + + const char * + GetOutput (); + + const char * + GetError (); + + size_t + PutOutput (FILE *fh); + + size_t + GetOutputSize (); + + size_t + GetErrorSize (); + + size_t + PutError (FILE *fh); + + void + Clear(); + + lldb::ReturnStatus + GetStatus(); + + void + SetStatus (lldb::ReturnStatus status); + + bool + Succeeded (); + + bool + HasResult (); + + void + AppendMessage (const char *message); + + void + AppendWarning (const char *message); + + bool + GetDescription (lldb::SBStream &description); + + void + SetImmediateOutputFile (FILE *fh); + + void + SetImmediateErrorFile (FILE *fh); + + void + PutCString(const char* string, int len = -1); + + size_t + Printf(const char* format, ...) __attribute__ ((format (printf, 2, 3))); + + const char * + GetOutput (bool only_if_no_immediate); + + const char * + GetError (bool only_if_no_immediate); + + void + SetError (lldb::SBError &error, + const char *fallback_error_cstr = NULL); + + void + SetError (const char* error_cstr); + +protected: + friend class SBCommandInterpreter; + friend class SBOptions; + + lldb_private::CommandReturnObject * + operator->() const; + + lldb_private::CommandReturnObject * + get() const; + + lldb_private::CommandReturnObject & + operator*() const; + + lldb_private::CommandReturnObject & + ref() const; + + void + SetLLDBObjectPtr (lldb_private::CommandReturnObject *ptr); + + private: + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBCommandReturnObject_h_ diff --git a/include/lldb/API/SBCommunication.h b/include/lldb/API/SBCommunication.h new file mode 100644 index 00000000000..ecaaa3523c9 --- /dev/null +++ b/include/lldb/API/SBCommunication.h @@ -0,0 +1,99 @@ +//===-- SBCommunication.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCommunication_h_ +#define LLDB_SBCommunication_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" + +namespace lldb { + +class SBCommunication +{ +public: + enum { + eBroadcastBitDisconnected = (1 << 0), ///< Sent when the communications connection is lost. + eBroadcastBitReadThreadGotBytes = (1 << 1), ///< Sent by the read thread when bytes become available. + eBroadcastBitReadThreadDidExit = (1 << 2), ///< Sent by the read thread when it exits to inform clients. + eBroadcastBitReadThreadShouldExit = (1 << 3), ///< Sent by clients that need to cancel the read thread. + eBroadcastBitPacketAvailable = (1 << 4), ///< Sent when data received makes a complete packet. + eAllEventBits = 0xffffffff + }; + + typedef void (*ReadThreadBytesReceived) (void *baton, const void *src, size_t src_len); + + SBCommunication (); + SBCommunication (const char * broadcaster_name); + ~SBCommunication (); + + + bool + IsValid () const; + + lldb::SBBroadcaster + GetBroadcaster (); + + static const char *GetBroadcasterClass(); + + lldb::ConnectionStatus + AdoptFileDesriptor (int fd, bool owns_fd); + + lldb::ConnectionStatus + Connect (const char *url); + + lldb::ConnectionStatus + Disconnect (); + + bool + IsConnected () const; + + bool + GetCloseOnEOF (); + + void + SetCloseOnEOF (bool b); + + size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status); + + size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status); + + bool + ReadThreadStart (); + + bool + ReadThreadStop (); + + bool + ReadThreadIsRunning (); + + bool + SetReadThreadBytesReceivedCallback (ReadThreadBytesReceived callback, + void *callback_baton); + + +private: + + DISALLOW_COPY_AND_ASSIGN (SBCommunication); + + lldb_private::Communication *m_opaque; + bool m_opaque_owned; +}; + + +} // namespace lldb + +#endif // LLDB_SBCommunication_h_ diff --git a/include/lldb/API/SBCompileUnit.h b/include/lldb/API/SBCompileUnit.h new file mode 100644 index 00000000000..95af3d4722c --- /dev/null +++ b/include/lldb/API/SBCompileUnit.h @@ -0,0 +1,116 @@ +//===-- SBCompileUnit.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBCompileUnit_h_ +#define LLDB_SBCompileUnit_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBCompileUnit +{ +public: + + SBCompileUnit (); + + SBCompileUnit (const lldb::SBCompileUnit &rhs); + + ~SBCompileUnit (); + + const lldb::SBCompileUnit & + operator = (const lldb::SBCompileUnit &rhs); + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetNumLineEntries () const; + + lldb::SBLineEntry + GetLineEntryAtIndex (uint32_t idx) const; + + uint32_t + FindLineEntryIndex (uint32_t start_idx, + uint32_t line, + lldb::SBFileSpec *inline_file_spec) const; + + uint32_t + FindLineEntryIndex (uint32_t start_idx, + uint32_t line, + lldb::SBFileSpec *inline_file_spec, + bool exact) const; + + SBFileSpec + GetSupportFileAtIndex (uint32_t idx) const; + + uint32_t + GetNumSupportFiles () const; + + uint32_t + FindSupportFileIndex (uint32_t start_idx, const SBFileSpec &sb_file, bool full); + + //------------------------------------------------------------------ + /// Get all types matching \a type_mask from debug info in this + /// compile unit. + /// + /// @param[in] type_mask + /// A bitfield that consists of one or more bits logically OR'ed + /// together from the lldb::TypeClass enumeration. This allows + /// you to request only structure types, or only class, struct + /// and union types. Passing in lldb::eTypeClassAny will return + /// all types found in the debug information for this compile + /// unit. + /// + /// @return + /// A list of types in this compile unit that match \a type_mask + //------------------------------------------------------------------ + lldb::SBTypeList + GetTypes (uint32_t type_mask = lldb::eTypeClassAny); + + bool + operator == (const lldb::SBCompileUnit &rhs) const; + + bool + operator != (const lldb::SBCompileUnit &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBSymbolContext; + friend class SBModule; + + SBCompileUnit (lldb_private::CompileUnit *lldb_object_ptr); + + const lldb_private::CompileUnit * + operator->() const; + + const lldb_private::CompileUnit & + operator*() const; + + lldb_private::CompileUnit * + get (); + + void + reset (lldb_private::CompileUnit *lldb_object_ptr); + + lldb_private::CompileUnit *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBCompileUnit_h_ diff --git a/include/lldb/API/SBData.h b/include/lldb/API/SBData.h new file mode 100644 index 00000000000..10c00224727 --- /dev/null +++ b/include/lldb/API/SBData.h @@ -0,0 +1,180 @@ +//===-- SBData.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBData_h_ +#define LLDB_SBData_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBData +{ +public: + + SBData (); + + SBData (const SBData &rhs); + + const SBData & + operator = (const SBData &rhs); + + ~SBData (); + + uint8_t + GetAddressByteSize (); + + void + SetAddressByteSize (uint8_t addr_byte_size); + + void + Clear (); + + bool + IsValid(); + + size_t + GetByteSize (); + + lldb::ByteOrder + GetByteOrder(); + + void + SetByteOrder (lldb::ByteOrder endian); + + float + GetFloat (lldb::SBError& error, lldb::offset_t offset); + + double + GetDouble (lldb::SBError& error, lldb::offset_t offset); + + long double + GetLongDouble (lldb::SBError& error, lldb::offset_t offset); + + lldb::addr_t + GetAddress (lldb::SBError& error, lldb::offset_t offset); + + uint8_t + GetUnsignedInt8 (lldb::SBError& error, lldb::offset_t offset); + + uint16_t + GetUnsignedInt16 (lldb::SBError& error, lldb::offset_t offset); + + uint32_t + GetUnsignedInt32 (lldb::SBError& error, lldb::offset_t offset); + + uint64_t + GetUnsignedInt64 (lldb::SBError& error, lldb::offset_t offset); + + int8_t + GetSignedInt8 (lldb::SBError& error, lldb::offset_t offset); + + int16_t + GetSignedInt16 (lldb::SBError& error, lldb::offset_t offset); + + int32_t + GetSignedInt32 (lldb::SBError& error, lldb::offset_t offset); + + int64_t + GetSignedInt64 (lldb::SBError& error, lldb::offset_t offset); + + const char* + GetString (lldb::SBError& error, lldb::offset_t offset); + + size_t + ReadRawData (lldb::SBError& error, + lldb::offset_t offset, + void *buf, + size_t size); + + bool + GetDescription (lldb::SBStream &description, lldb::addr_t base_addr = LLDB_INVALID_ADDRESS); + + // it would be nice to have SetData(SBError, const void*, size_t) when endianness and address size can be + // inferred from the existing DataExtractor, but having two SetData() signatures triggers a SWIG bug where + // the typemap isn't applied before resolving the overload, and thus the right function never gets called + void + SetData (lldb::SBError& error, const void *buf, size_t size, lldb::ByteOrder endian, uint8_t addr_size); + + // see SetData() for why we don't have Append(const void* buf, size_t size) + bool + Append (const SBData& rhs); + + static lldb::SBData + CreateDataFromCString (lldb::ByteOrder endian, uint32_t addr_byte_size, const char* data); + + // in the following CreateData*() and SetData*() prototypes, the two parameters array and array_len + // should not be renamed or rearranged, because doing so will break the SWIG typemap + static lldb::SBData + CreateDataFromUInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint64_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromUInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint32_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromSInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int64_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromSInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int32_t* array, size_t array_len); + + static lldb::SBData + CreateDataFromDoubleArray (lldb::ByteOrder endian, uint32_t addr_byte_size, double* array, size_t array_len); + + bool + SetDataFromCString (const char* data); + + bool + SetDataFromUInt64Array (uint64_t* array, size_t array_len); + + bool + SetDataFromUInt32Array (uint32_t* array, size_t array_len); + + bool + SetDataFromSInt64Array (int64_t* array, size_t array_len); + + bool + SetDataFromSInt32Array (int32_t* array, size_t array_len); + + bool + SetDataFromDoubleArray (double* array, size_t array_len); + + +protected: + + // Mimic shared pointer... + lldb_private::DataExtractor * + get() const; + + lldb_private::DataExtractor * + operator->() const; + + lldb::DataExtractorSP & + operator*(); + + const lldb::DataExtractorSP & + operator*() const; + + SBData (const lldb::DataExtractorSP &data_sp); + + void + SetOpaque (const lldb::DataExtractorSP &data_sp); + +private: + friend class SBInstruction; + friend class SBProcess; + friend class SBSection; + friend class SBValue; + + lldb::DataExtractorSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBData_h_ diff --git a/include/lldb/API/SBDebugger.h b/include/lldb/API/SBDebugger.h new file mode 100644 index 00000000000..518cbf67c93 --- /dev/null +++ b/include/lldb/API/SBDebugger.h @@ -0,0 +1,339 @@ +//===-- SBDebugger.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBDebugger_h_ +#define LLDB_SBDebugger_h_ + +#include "lldb/API/SBDefines.h" +#include + +namespace lldb { + +class SBDebugger +{ +public: + + static void + Initialize(); + + static void + Terminate(); + + // Deprecated, use the one that takes a source_init_files bool. + static lldb::SBDebugger + Create(); + + static lldb::SBDebugger + Create(bool source_init_files); + + static lldb::SBDebugger + Create(bool source_init_files, lldb::LogOutputCallback log_callback, void *baton); + + static void + Destroy (lldb::SBDebugger &debugger); + + static void + MemoryPressureDetected (); + + SBDebugger(); + + SBDebugger(const lldb::SBDebugger &rhs); + + SBDebugger(const lldb::DebuggerSP &debugger_sp); + + lldb::SBDebugger & + operator = (const lldb::SBDebugger &rhs); + + ~SBDebugger(); + + bool + IsValid() const; + + void + Clear (); + + void + SetAsync (bool b); + + bool + GetAsync (); + + void + SkipLLDBInitFiles (bool b); + + void + SkipAppInitFiles (bool b); + + void + SetInputFileHandle (FILE *f, bool transfer_ownership); + + void + SetOutputFileHandle (FILE *f, bool transfer_ownership); + + void + SetErrorFileHandle (FILE *f, bool transfer_ownership); + + FILE * + GetInputFileHandle (); + + FILE * + GetOutputFileHandle (); + + FILE * + GetErrorFileHandle (); + + void + SaveInputTerminalState(); + + void + RestoreInputTerminalState(); + + lldb::SBCommandInterpreter + GetCommandInterpreter (); + + void + HandleCommand (const char *command); + + lldb::SBListener + GetListener (); + + void + HandleProcessEvent (const lldb::SBProcess &process, + const lldb::SBEvent &event, + FILE *out, + FILE *err); + + lldb::SBTarget + CreateTarget (const char *filename, + const char *target_triple, + const char *platform_name, + bool add_dependent_modules, + lldb::SBError& error); + + lldb::SBTarget + CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple); + + lldb::SBTarget + CreateTargetWithFileAndArch (const char *filename, + const char *archname); + + lldb::SBTarget + CreateTarget (const char *filename); + + // Return true if target is deleted from the target list of the debugger. + bool + DeleteTarget (lldb::SBTarget &target); + + lldb::SBTarget + GetTargetAtIndex (uint32_t idx); + + uint32_t + GetIndexOfTarget (lldb::SBTarget target); + + lldb::SBTarget + FindTargetWithProcessID (pid_t pid); + + lldb::SBTarget + FindTargetWithFileAndArch (const char *filename, + const char *arch); + + uint32_t + GetNumTargets (); + + lldb::SBTarget + GetSelectedTarget (); + + void + SetSelectedTarget (SBTarget& target); + + lldb::SBSourceManager + GetSourceManager (); + + // REMOVE: just for a quick fix, need to expose platforms through + // SBPlatform from this class. + lldb::SBError + SetCurrentPlatform (const char *platform_name); + + bool + SetCurrentPlatformSDKRoot (const char *sysroot); + + // FIXME: Once we get the set show stuff in place, the driver won't need + // an interface to the Set/Get UseExternalEditor. + bool + SetUseExternalEditor (bool input); + + bool + GetUseExternalEditor (); + + bool + SetUseColor (bool use_color); + + bool + GetUseColor () const; + + static bool + GetDefaultArchitecture (char *arch_name, size_t arch_name_len); + + static bool + SetDefaultArchitecture (const char *arch_name); + + lldb::ScriptLanguage + GetScriptingLanguage (const char *script_language_name); + + static const char * + GetVersionString (); + + static const char * + StateAsCString (lldb::StateType state); + + static bool + StateIsRunningState (lldb::StateType state); + + static bool + StateIsStoppedState (lldb::StateType state); + + bool + EnableLog (const char *channel, const char **categories); + + void + SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton); + + // DEPRECATED + void + DispatchInput (void* baton, + const void* data, + size_t data_len); + + void + DispatchInput (const void *data, size_t data_len); + + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + + void + PushInputReader (lldb::SBInputReader &reader); + + void + NotifyTopInputReader (lldb::InputReaderAction notification); + + bool + InputReaderIsTopReader (const lldb::SBInputReader &reader); + + const char * + GetInstanceName (); + + static SBDebugger + FindDebuggerWithID (int id); + + static lldb::SBError + SetInternalVariable (const char *var_name, const char *value, const char *debugger_instance_name); + + static lldb::SBStringList + GetInternalVariableValue (const char *var_name, const char *debugger_instance_name); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetTerminalWidth () const; + + void + SetTerminalWidth (uint32_t term_width); + + lldb::user_id_t + GetID (); + + const char * + GetPrompt() const; + + void + SetPrompt (const char *prompt); + + lldb::ScriptLanguage + GetScriptLanguage() const; + + void + SetScriptLanguage (lldb::ScriptLanguage script_lang); + + bool + GetCloseInputOnEOF () const; + + void + SetCloseInputOnEOF (bool b); + + SBTypeCategory + GetCategory (const char* category_name); + + SBTypeCategory + CreateCategory (const char* category_name); + + bool + DeleteCategory (const char* category_name); + + uint32_t + GetNumCategories (); + + SBTypeCategory + GetCategoryAtIndex (uint32_t); + + SBTypeCategory + GetDefaultCategory(); + + SBTypeFormat + GetFormatForType (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSummary + GetSummaryForType (SBTypeNameSpecifier); +#endif + + SBTypeFilter + GetFilterForType (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSynthetic + GetSyntheticForType (SBTypeNameSpecifier); +#endif + +private: + + friend class SBCommandInterpreter; + friend class SBInputReader; + friend class SBListener; + friend class SBProcess; + friend class SBSourceManager; + friend class SBTarget; + + lldb::SBTarget + FindTargetWithLLDBProcess (const lldb::ProcessSP &processSP); + + void + reset (const lldb::DebuggerSP &debugger_sp); + + lldb_private::Debugger * + get () const; + + lldb_private::Debugger & + ref () const; + + const lldb::DebuggerSP & + get_sp () const; + + lldb::DebuggerSP m_opaque_sp; + +}; // class SBDebugger + + +} // namespace lldb + +#endif // LLDB_SBDebugger_h_ diff --git a/include/lldb/API/SBDeclaration.h b/include/lldb/API/SBDeclaration.h new file mode 100644 index 00000000000..190026c0d2d --- /dev/null +++ b/include/lldb/API/SBDeclaration.h @@ -0,0 +1,89 @@ +//===-- SBDeclaration.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBDeclaration_h_ +#define LLDB_SBDeclaration_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + + class SBDeclaration + { + public: + + SBDeclaration (); + + SBDeclaration (const lldb::SBDeclaration &rhs); + + ~SBDeclaration (); + + const lldb::SBDeclaration & + operator = (const lldb::SBDeclaration &rhs); + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + + void + SetFileSpec (lldb::SBFileSpec filespec); + + void + SetLine (uint32_t line); + + void + SetColumn (uint32_t column); + + bool + operator == (const lldb::SBDeclaration &rhs) const; + + bool + operator != (const lldb::SBDeclaration &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + + protected: + + lldb_private::Declaration * + get (); + + private: + friend class SBValue; + + const lldb_private::Declaration * + operator->() const; + + lldb_private::Declaration & + ref(); + + const lldb_private::Declaration & + ref() const; + + SBDeclaration (const lldb_private::Declaration *lldb_object_ptr); + + void + SetDeclaration (const lldb_private::Declaration &lldb_object_ref); + + std::unique_ptr m_opaque_ap; + }; + + +} // namespace lldb + +#endif // LLDB_SBDeclaration_h_ diff --git a/include/lldb/API/SBDefines.h b/include/lldb/API/SBDefines.h new file mode 100644 index 00000000000..2cdf92170d8 --- /dev/null +++ b/include/lldb/API/SBDefines.h @@ -0,0 +1,84 @@ +//===-- SBDefines.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBDefines_h_ +#define LLDB_SBDefines_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" +#include "lldb/lldb-versioning.h" + +// Forward Declarations + +namespace lldb { + +class SBAddress; +class SBBlock; +class SBBreakpoint; +class SBBreakpointLocation; +class SBBroadcaster; +class SBCommand; +class SBCommandInterpreter; +class SBCommandPluginInterface; +class SBCommandReturnObject; +class SBCommunication; +class SBCompileUnit; +class SBData; +class SBDebugger; +class SBDeclaration; +class SBError; +class SBEvent; +class SBEventList; +class SBExpressionOptions; +class SBFileSpec; +class SBFileSpecList; +class SBFrame; +class SBFunction; +class SBHostOS; +class SBInputReader; +class SBInstruction; +class SBInstructionList; +class SBLineEntry; +class SBListener; +class SBModule; +class SBModuleSpec; +class SBModuleSpecList; +class SBProcess; +class SBSourceManager; +class SBStream; +class SBStringList; +class SBSymbol; +class SBSymbolContext; +class SBSymbolContextList; +class SBTarget; +class SBThread; +class SBType; +class SBTypeCategory; +class SBTypeFilter; +class SBTypeFormat; +class SBTypeNameSpecifier; +class SBTypeSummary; +#ifndef LLDB_DISABLE_PYTHON +class SBTypeSynthetic; +#endif +class SBTypeList; +class SBValue; +class SBValueList; +class SBWatchpoint; + +} + +#endif // LLDB_SBDefines_h_ diff --git a/include/lldb/API/SBError.h b/include/lldb/API/SBError.h new file mode 100644 index 00000000000..a6d3dacb454 --- /dev/null +++ b/include/lldb/API/SBError.h @@ -0,0 +1,106 @@ +//===-- SBError.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBError_h_ +#define LLDB_SBError_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBError { +public: + SBError (); + + SBError (const lldb::SBError &rhs); + + ~SBError(); + + const SBError & + operator =(const lldb::SBError &rhs); + + const char * + GetCString () const; + + void + Clear (); + + bool + Fail () const; + + bool + Success () const; + + uint32_t + GetError () const; + + lldb::ErrorType + GetType () const; + + void + SetError (uint32_t err, lldb::ErrorType type); + + void + SetErrorToErrno (); + + void + SetErrorToGenericError (); + + void + SetErrorString (const char *err_str); + + int + SetErrorStringWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + bool + IsValid () const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + + friend class SBCommandReturnObject; + friend class SBData; + friend class SBDebugger; + friend class SBCommunication; + friend class SBHostOS; + friend class SBInputReader; + friend class SBProcess; + friend class SBThread; + friend class SBTarget; + friend class SBValue; + friend class SBWatchpoint; + + lldb_private::Error * + get(); + + lldb_private::Error * + operator->(); + + const lldb_private::Error & + operator*() const; + + lldb_private::Error & + ref(); + + void + SetError (const lldb_private::Error &lldb_error); + +private: + std::unique_ptr m_opaque_ap; + + void + CreateIfNeeded (); +}; + + +} // namespace lldb + +#endif // LLDB_SBError_h_ diff --git a/include/lldb/API/SBEvent.h b/include/lldb/API/SBEvent.h new file mode 100644 index 00000000000..6cb975a1ff7 --- /dev/null +++ b/include/lldb/API/SBEvent.h @@ -0,0 +1,102 @@ +//===-- SBEvent.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBEvent_h_ +#define LLDB_SBEvent_h_ + +#include "lldb/API/SBDefines.h" + +#include +#include + + +namespace lldb { + +class SBBroadcaster; + +class SBEvent +{ +public: + SBEvent(); + + SBEvent (const lldb::SBEvent &rhs); + + // Make an event that contains a C string. + SBEvent (uint32_t event, const char *cstr, uint32_t cstr_len); + + ~SBEvent(); + + const SBEvent & + operator = (const lldb::SBEvent &rhs); + + bool + IsValid() const; + + const char * + GetDataFlavor (); + + uint32_t + GetType () const; + + lldb::SBBroadcaster + GetBroadcaster () const; + + const char * + GetBroadcasterClass () const; + + bool + BroadcasterMatchesPtr (const lldb::SBBroadcaster *broadcaster); + + bool + BroadcasterMatchesRef (const lldb::SBBroadcaster &broadcaster); + + void + Clear(); + + static const char * + GetCStringFromEvent (const lldb::SBEvent &event); + + bool + GetDescription (lldb::SBStream &description); + + bool + GetDescription (lldb::SBStream &description) const; + +protected: + friend class SBListener; + friend class SBBroadcaster; + friend class SBBreakpoint; + friend class SBDebugger; + friend class SBProcess; + friend class SBThread; + friend class SBWatchpoint; + + SBEvent (lldb::EventSP &event_sp); + + lldb::EventSP & + GetSP () const; + + void + reset (lldb::EventSP &event_sp); + + void + reset (lldb_private::Event* event); + + lldb_private::Event * + get () const; + +private: + + mutable lldb::EventSP m_event_sp; + mutable lldb_private::Event *m_opaque_ptr; +}; + +} // namespace lldb + +#endif // LLDB_SBEvent_h_ diff --git a/include/lldb/API/SBExpressionOptions.h b/include/lldb/API/SBExpressionOptions.h new file mode 100644 index 00000000000..eed9ed528be --- /dev/null +++ b/include/lldb/API/SBExpressionOptions.h @@ -0,0 +1,89 @@ +//===-- SBEvent.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBExpressionOptions_h_ +#define LLDB_SBExpressionOptions_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + + +class SBExpressionOptions +{ +public: + SBExpressionOptions(); + + SBExpressionOptions (const lldb::SBExpressionOptions &rhs); + + ~SBExpressionOptions(); + + const SBExpressionOptions & + operator = (const lldb::SBExpressionOptions &rhs); + + bool + GetCoerceResultToId () const; + + void + SetCoerceResultToId (bool coerce = true); + + bool + GetUnwindOnError () const; + + void + SetUnwindOnError (bool unwind = true); + + bool + GetIgnoreBreakpoints () const; + + void + SetIgnoreBreakpoints (bool ignore = true); + + lldb::DynamicValueType + GetFetchDynamicValue () const; + + void + SetFetchDynamicValue (lldb::DynamicValueType dynamic = lldb::eDynamicCanRunTarget); + + uint32_t + GetTimeoutInMicroSeconds () const; + + void + SetTimeoutInMicroSeconds (uint32_t timeout = 0); + + bool + GetTryAllThreads () const; + + void + SetTryAllThreads (bool run_others = true); + +protected: + + SBExpressionOptions (lldb_private::EvaluateExpressionOptions &expression_options); + + lldb_private::EvaluateExpressionOptions * + get () const; + + lldb_private::EvaluateExpressionOptions & + ref () const; + + friend class SBFrame; + friend class SBValue; + friend class SBTarget; + +private: + // This auto_pointer is made in the constructor and is always valid. + mutable std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBExpressionOptions_h_ diff --git a/include/lldb/API/SBFileSpec.h b/include/lldb/API/SBFileSpec.h new file mode 100644 index 00000000000..e44abe4759c --- /dev/null +++ b/include/lldb/API/SBFileSpec.h @@ -0,0 +1,96 @@ +//===-- SBFileSpec.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFileSpec_h_ +#define LLDB_SBFileSpec_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBFileSpec +{ +public: + SBFileSpec (); + + SBFileSpec (const lldb::SBFileSpec &rhs); + + SBFileSpec (const char *path);// Deprected, use SBFileSpec (const char *path, bool resolve) + + SBFileSpec (const char *path, bool resolve); + + ~SBFileSpec (); + + const SBFileSpec & + operator = (const lldb::SBFileSpec &rhs); + + bool + IsValid() const; + + bool + Exists () const; + + bool + ResolveExecutableLocation (); + + const char * + GetFilename() const; + + const char * + GetDirectory() const; + + uint32_t + GetPath (char *dst_path, size_t dst_len) const; + + static int + ResolvePath (const char *src_path, char *dst_path, size_t dst_len); + + bool + GetDescription (lldb::SBStream &description) const; + +private: + friend class SBAttachInfo; + friend class SBBlock; + friend class SBCompileUnit; + friend class SBDeclaration; + friend class SBFileSpecList; + friend class SBHostOS; + friend class SBLaunchInfo; + friend class SBLineEntry; + friend class SBModule; + friend class SBModuleSpec; + friend class SBProcess; + friend class SBSourceManager; + friend class SBThread; + friend class SBTarget; + + SBFileSpec (const lldb_private::FileSpec& fspec); + + void + SetFileSpec (const lldb_private::FileSpec& fspec); + + const lldb_private::FileSpec * + operator->() const; + + const lldb_private::FileSpec * + get() const; + + const lldb_private::FileSpec & + operator*() const; + + const lldb_private::FileSpec & + ref() const; + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBFileSpec_h_ diff --git a/include/lldb/API/SBFileSpecList.h b/include/lldb/API/SBFileSpecList.h new file mode 100644 index 00000000000..734e7d4d35c --- /dev/null +++ b/include/lldb/API/SBFileSpecList.h @@ -0,0 +1,72 @@ +//===-- SBFileSpecList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFileSpecList_h_ +#define LLDB_SBFileSpecList_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBFileSpecList +{ +public: + SBFileSpecList (); + + SBFileSpecList (const lldb::SBFileSpecList &rhs); + + ~SBFileSpecList (); + + const SBFileSpecList & + operator = (const lldb::SBFileSpecList &rhs); + + uint32_t + GetSize () const; + + bool + GetDescription (SBStream &description) const; + + void + Append (const SBFileSpec &sb_file); + + bool + AppendIfUnique (const SBFileSpec &sb_file); + + void + Clear(); + + uint32_t + FindFileIndex (uint32_t idx, const SBFileSpec &sb_file, bool full); + + const SBFileSpec + GetFileSpecAtIndex (uint32_t idx) const; + +private: + +friend class SBTarget; + + const lldb_private::FileSpecList * + operator->() const; + + const lldb_private::FileSpecList * + get() const; + + const lldb_private::FileSpecList & + operator*() const; + + const lldb_private::FileSpecList & + ref() const; + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBFileSpecList_h_ diff --git a/include/lldb/API/SBFrame.h b/include/lldb/API/SBFrame.h new file mode 100644 index 00000000000..4ae38c13bed --- /dev/null +++ b/include/lldb/API/SBFrame.h @@ -0,0 +1,242 @@ +//===-- SBFrame.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFrame_h_ +#define LLDB_SBFrame_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBValueList.h" + +namespace lldb { + +class SBFrame +{ +public: + SBFrame (); + + SBFrame (const lldb::SBFrame &rhs); + + const lldb::SBFrame & + operator =(const lldb::SBFrame &rhs); + + ~SBFrame(); + + bool + IsEqual (const lldb::SBFrame &that) const; + + bool + IsValid() const; + + uint32_t + GetFrameID () const; + + lldb::addr_t + GetPC () const; + + bool + SetPC (lldb::addr_t new_pc); + + lldb::addr_t + GetSP () const; + + lldb::addr_t + GetFP () const; + + lldb::SBAddress + GetPCAddress () const; + + lldb::SBSymbolContext + GetSymbolContext (uint32_t resolve_scope) const; + + lldb::SBModule + GetModule () const; + + lldb::SBCompileUnit + GetCompileUnit () const; + + lldb::SBFunction + GetFunction () const; + + lldb::SBSymbol + GetSymbol () const; + + /// Gets the deepest block that contains the frame PC. + /// + /// See also GetFrameBlock(). + lldb::SBBlock + GetBlock () const; + + /// Get the appropriate function name for this frame. Inlined functions in + /// LLDB are represented by Blocks that have inlined function information, so + /// just looking at the SBFunction or SBSymbol for a frame isn't enough. + /// This function will return the appriopriate function, symbol or inlined + /// function name for the frame. + /// + /// This function returns: + /// - the name of the inlined function (if there is one) + /// - the name of the concrete function (if there is one) + /// - the name of the symbol (if there is one) + /// - NULL + /// + /// See also IsInlined(). + const char * + GetFunctionName(); + + /// Return true if this frame represents an inlined function. + /// + /// See also GetFunctionName(). + bool + IsInlined(); + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + EvaluateExpression (const char *expr); + + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic); + + lldb::SBValue + EvaluateExpression (const char *expr, lldb::DynamicValueType use_dynamic, bool unwind_on_error); + + lldb::SBValue + EvaluateExpression (const char *expr, const SBExpressionOptions &options); + + /// Gets the lexical block that defines the stack frame. Another way to think + /// of this is it will return the block that contains all of the variables + /// for a stack frame. Inlined functions are represented as SBBlock objects + /// that have inlined function information: the name of the inlined function, + /// where it was called from. The block that is returned will be the first + /// block at or above the block for the PC (SBFrame::GetBlock()) that defines + /// the scope of the frame. When a function contains no inlined functions, + /// this will be the top most lexical block that defines the function. + /// When a function has inlined functions and the PC is currently + /// in one of those inlined functions, this method will return the inlined + /// block that defines this frame. If the PC isn't currently in an inlined + /// function, the lexical block that defines the function is returned. + lldb::SBBlock + GetFrameBlock () const; + + lldb::SBLineEntry + GetLineEntry () const; + + lldb::SBThread + GetThread () const; + + const char * + Disassemble () const; + + void + Clear(); + + bool + operator == (const lldb::SBFrame &rhs) const; + + bool + operator != (const lldb::SBFrame &rhs) const; + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only); + + lldb::SBValueList + GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only, + lldb::DynamicValueType use_dynamic); + + lldb::SBValueList + GetRegisters (); + + lldb::SBValue + FindRegister (const char *name); + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + FindVariable (const char *var_name); + + lldb::SBValue + FindVariable (const char *var_name, lldb::DynamicValueType use_dynamic); + + // Find a value for a variable expression path like "rect.origin.x" or + // "pt_ptr->x", "*self", "*this->obj_ptr". The returned value is _not_ + // and expression result and is not a constant object like + // SBFrame::EvaluateExpression(...) returns, but a child object of + // the variable value. + lldb::SBValue + GetValueForVariablePath (const char *var_expr_cstr, + DynamicValueType use_dynamic); + + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + GetValueForVariablePath (const char *var_path); + + /// Find variables, register sets, registers, or persistent variables using + /// the frame as the scope. + /// + /// NB. This function does not look up ivars in the function object pointer. + /// To do that use GetValueForVariablePath. + /// + /// The version that doesn't supply a 'use_dynamic' value will use the + /// target's default. + lldb::SBValue + FindValue (const char *name, ValueType value_type); + + lldb::SBValue + FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic); + + /// Find and watch a variable using the frame as the scope. + /// It returns an SBValue, similar to FindValue() method, if find-and-watch + /// operation succeeds. Otherwise, an invalid SBValue is returned. + /// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. + lldb::SBValue + WatchValue (const char *name, ValueType value_type, uint32_t watch_type); + + /// Find and watch the location pointed to by a variable using the frame as + /// the scope. + /// It returns an SBValue, similar to FindValue() method, if find-and-watch + /// operation succeeds. Otherwise, an invalid SBValue is returned. + /// You can use LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE for 'rw' watch. + lldb::SBValue + WatchLocation (const char *name, ValueType value_type, uint32_t watch_type, size_t size); + + bool + GetDescription (lldb::SBStream &description); + + SBFrame (const lldb::StackFrameSP &lldb_object_sp); + +protected: + + friend class SBBlock; + friend class SBInstruction; + friend class SBThread; + friend class SBValue; +#ifndef LLDB_DISABLE_PYTHON + friend class lldb_private::ScriptInterpreterPython; +#endif + + lldb::StackFrameSP + GetFrameSP() const; + + void + SetFrameSP (const lldb::StackFrameSP &lldb_object_sp); + + lldb::ExecutionContextRefSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBFrame_h_ diff --git a/include/lldb/API/SBFunction.h b/include/lldb/API/SBFunction.h new file mode 100644 index 00000000000..49a3847efbe --- /dev/null +++ b/include/lldb/API/SBFunction.h @@ -0,0 +1,93 @@ +//===-- SBFunction.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBFunction_h_ +#define LLDB_SBFunction_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBInstructionList.h" + +namespace lldb { + +class SBFunction +{ +public: + + SBFunction (); + + SBFunction (const lldb::SBFunction &rhs); + + const lldb::SBFunction & + operator = (const lldb::SBFunction &rhs); + + ~SBFunction (); + + bool + IsValid () const; + + const char * + GetName() const; + + const char * + GetMangledName () const; + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target); + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target, const char *flavor); + + lldb::SBAddress + GetStartAddress (); + + lldb::SBAddress + GetEndAddress (); + + uint32_t + GetPrologueByteSize (); + + lldb::SBType + GetType (); + + lldb::SBBlock + GetBlock (); + + bool + operator == (const lldb::SBFunction &rhs) const; + + bool + operator != (const lldb::SBFunction &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + + lldb_private::Function * + get (); + + void + reset (lldb_private::Function *lldb_object_ptr); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBSymbolContext; + + SBFunction (lldb_private::Function *lldb_object_ptr); + + + lldb_private::Function *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBFunction_h_ diff --git a/include/lldb/API/SBHostOS.h b/include/lldb/API/SBHostOS.h new file mode 100644 index 00000000000..52754ea4e82 --- /dev/null +++ b/include/lldb/API/SBHostOS.h @@ -0,0 +1,57 @@ +//===-- SBHostOS.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBHostOS_h_ +#define LLDB_SBHostOS_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBHostOS +{ +public: + + static lldb::SBFileSpec + GetProgramFileSpec (); + + static lldb::SBFileSpec + GetLLDBPythonPath (); + + static void + ThreadCreated (const char *name); + + static lldb::thread_t + ThreadCreate (const char *name, + void *(*thread_function)(void *), + void *thread_arg, + lldb::SBError *err); + + static bool + ThreadCancel (lldb::thread_t thread, + lldb::SBError *err); + + static bool + ThreadDetach (lldb::thread_t thread, + lldb::SBError *err); + static bool + ThreadJoin (lldb::thread_t thread, + void **result, + lldb::SBError *err); + + +private: + +}; + + +} // namespace lldb + +#endif // LLDB_SBHostOS_h_ diff --git a/include/lldb/API/SBInputReader.h b/include/lldb/API/SBInputReader.h new file mode 100644 index 00000000000..61f7de4fde4 --- /dev/null +++ b/include/lldb/API/SBInputReader.h @@ -0,0 +1,97 @@ +//===-- SBInputReader.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInputReader_h_ +#define LLDB_SBInputReader_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBInputReader +{ +public: + + typedef size_t (*Callback) (void *baton, + SBInputReader *reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + SBInputReader (); + + SBInputReader (const lldb::InputReaderSP &reader_sp); + + SBInputReader (const lldb::SBInputReader &rhs); + + ~SBInputReader (); + + + SBError + Initialize (SBDebugger &debugger, + Callback callback, + void *callback_baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo); + + bool + IsValid () const; + + const lldb::SBInputReader & + operator = (const lldb::SBInputReader &rhs); + + bool + IsActive () const; + + bool + IsDone () const; + + void + SetIsDone (bool value); + + InputReaderGranularity + GetGranularity (); + +protected: + friend class SBDebugger; + + lldb_private::InputReader * + operator->() const; + + lldb::InputReaderSP & + operator *(); + + const lldb::InputReaderSP & + operator *() const; + + lldb_private::InputReader * + get() const; + + lldb_private::InputReader & + ref() const; + +private: + + static size_t + PrivateCallback (void *baton, + lldb_private::InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + lldb::InputReaderSP m_opaque_sp; + Callback m_callback_function; + void *m_callback_baton; +}; + +} // namespace lldb + +#endif // LLDB_SBInputReader_h_ diff --git a/include/lldb/API/SBInstruction.h b/include/lldb/API/SBInstruction.h new file mode 100644 index 00000000000..aad2d87f4f3 --- /dev/null +++ b/include/lldb/API/SBInstruction.h @@ -0,0 +1,94 @@ +//===-- SBInstruction.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInstruction_h_ +#define LLDB_SBInstruction_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBData.h" + +#include + +// There's a lot to be fixed here, but need to wait for underlying insn implementation +// to be revised & settle down first. + +namespace lldb { + +class SBInstruction +{ +public: + + SBInstruction (); + + SBInstruction (const SBInstruction &rhs); + + const SBInstruction & + operator = (const SBInstruction &rhs); + + ~SBInstruction (); + + bool + IsValid(); + + SBAddress + GetAddress(); + + lldb::AddressClass + GetAddressClass (); + + const char * + GetMnemonic (lldb::SBTarget target); + + const char * + GetOperands (lldb::SBTarget target); + + const char * + GetComment (lldb::SBTarget target); + + lldb::SBData + GetData (lldb::SBTarget target); + + size_t + GetByteSize (); + + bool + DoesBranch (); + + void + Print (FILE *out); + + bool + GetDescription (lldb::SBStream &description); + + bool + EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options); + + bool + DumpEmulation (const char * triple); // triple is to specify the architecture, e.g. 'armv6' or 'armv7-apple-ios' + + bool + TestEmulation (lldb::SBStream &output_stream, const char *test_file); + +protected: + friend class SBInstructionList; + + SBInstruction (const lldb::InstructionSP &inst_sp); + + void + SetOpaque (const lldb::InstructionSP &inst_sp); + +private: + + lldb::InstructionSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBInstruction_h_ diff --git a/include/lldb/API/SBInstructionList.h b/include/lldb/API/SBInstructionList.h new file mode 100644 index 00000000000..944e144a148 --- /dev/null +++ b/include/lldb/API/SBInstructionList.h @@ -0,0 +1,71 @@ +//===-- SBInstructionList.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBInstructionList_h_ +#define LLDB_SBInstructionList_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +class SBInstructionList +{ +public: + + SBInstructionList (); + + SBInstructionList (const SBInstructionList &rhs); + + const SBInstructionList & + operator = (const SBInstructionList &rhs); + + ~SBInstructionList (); + + bool + IsValid () const; + + size_t + GetSize (); + + lldb::SBInstruction + GetInstructionAtIndex (uint32_t idx); + + void + Clear (); + + void + AppendInstruction (lldb::SBInstruction inst); + + void + Print (FILE *out); + + bool + GetDescription (lldb::SBStream &description); + + bool + DumpEmulationForAllInstructions (const char *triple); + +protected: + friend class SBFunction; + friend class SBSymbol; + friend class SBTarget; + + void + SetDisassembler (const lldb::DisassemblerSP &opaque_sp); + +private: + lldb::DisassemblerSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBInstructionList_h_ diff --git a/include/lldb/API/SBLineEntry.h b/include/lldb/API/SBLineEntry.h new file mode 100644 index 00000000000..2d099a29798 --- /dev/null +++ b/include/lldb/API/SBLineEntry.h @@ -0,0 +1,99 @@ +//===-- SBLineEntry.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBLineEntry_h_ +#define LLDB_SBLineEntry_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBLineEntry +{ +public: + + SBLineEntry (); + + SBLineEntry (const lldb::SBLineEntry &rhs); + + ~SBLineEntry (); + + const lldb::SBLineEntry & + operator = (const lldb::SBLineEntry &rhs); + + lldb::SBAddress + GetStartAddress () const; + + lldb::SBAddress + GetEndAddress () const; + + bool + IsValid () const; + + lldb::SBFileSpec + GetFileSpec () const; + + uint32_t + GetLine () const; + + uint32_t + GetColumn () const; + + void + SetFileSpec (lldb::SBFileSpec filespec); + + void + SetLine (uint32_t line); + + void + SetColumn (uint32_t column); + + bool + operator == (const lldb::SBLineEntry &rhs) const; + + bool + operator != (const lldb::SBLineEntry &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + + lldb_private::LineEntry * + get (); + +private: + friend class SBAddress; + friend class SBCompileUnit; + friend class SBFrame; + friend class SBSymbolContext; + + const lldb_private::LineEntry * + operator->() const; + + lldb_private::LineEntry & + ref(); + + const lldb_private::LineEntry & + ref() const; + + SBLineEntry (const lldb_private::LineEntry *lldb_object_ptr); + + void + SetLineEntry (const lldb_private::LineEntry &lldb_object_ref); + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBLineEntry_h_ diff --git a/include/lldb/API/SBListener.h b/include/lldb/API/SBListener.h new file mode 100644 index 00000000000..c5a04734174 --- /dev/null +++ b/include/lldb/API/SBListener.h @@ -0,0 +1,135 @@ +//===-- SBListener.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBListener_h_ +#define LLDB_SBListener_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBListener +{ +public: + SBListener (); + + SBListener (const char *name); + + SBListener (const SBListener &rhs); + + ~SBListener (); + + const lldb::SBListener & + operator = (const lldb::SBListener &rhs); + + void + AddEvent (const lldb::SBEvent &event); + + void + Clear (); + + bool + IsValid () const; + + uint32_t + StartListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask); + + bool + StopListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask); + + uint32_t + StartListeningForEvents (const lldb::SBBroadcaster& broadcaster, + uint32_t event_mask); + + bool + StopListeningForEvents (const lldb::SBBroadcaster& broadcaster, + uint32_t event_mask); + + // Returns true if an event was recieved, false if we timed out. + bool + WaitForEvent (uint32_t num_seconds, + lldb::SBEvent &event); + + bool + WaitForEventForBroadcaster (uint32_t num_seconds, + const lldb::SBBroadcaster &broadcaster, + lldb::SBEvent &sb_event); + + bool + WaitForEventForBroadcasterWithType (uint32_t num_seconds, + const lldb::SBBroadcaster &broadcaster, + uint32_t event_type_mask, + lldb::SBEvent &sb_event); + + bool + PeekAtNextEvent (lldb::SBEvent &sb_event); + + bool + PeekAtNextEventForBroadcaster (const lldb::SBBroadcaster &broadcaster, + lldb::SBEvent &sb_event); + + bool + PeekAtNextEventForBroadcasterWithType (const lldb::SBBroadcaster &broadcaster, + uint32_t event_type_mask, + lldb::SBEvent &sb_event); + + bool + GetNextEvent (lldb::SBEvent &sb_event); + + bool + GetNextEventForBroadcaster (const lldb::SBBroadcaster &broadcaster, + lldb::SBEvent &sb_event); + + bool + GetNextEventForBroadcasterWithType (const lldb::SBBroadcaster &broadcaster, + uint32_t event_type_mask, + lldb::SBEvent &sb_event); + + bool + HandleBroadcastEvent (const lldb::SBEvent &event); + +protected: + friend class SBBroadcaster; + friend class SBCommandInterpreter; + friend class SBDebugger; + friend class SBTarget; + + SBListener (lldb_private::Listener &listener); + +private: + + lldb_private::Listener * + operator->() const; + + lldb_private::Listener * + get() const; + + lldb_private::Listener & + ref() const; + + lldb_private::Listener & + operator *(); + + const lldb_private::Listener & + operator *() const; + + void + reset(lldb_private::Listener *listener, bool transfer_ownership); + + lldb::ListenerSP m_opaque_sp; + lldb_private::Listener *m_opaque_ptr; +}; + +} // namespace lldb + +#endif // LLDB_SBListener_h_ diff --git a/include/lldb/API/SBModule.h b/include/lldb/API/SBModule.h new file mode 100644 index 00000000000..a3c4879aa2f --- /dev/null +++ b/include/lldb/API/SBModule.h @@ -0,0 +1,287 @@ +//===-- SBModule.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBModule_h_ +#define LLDB_SBModule_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBSection.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBValueList.h" + +namespace lldb { + +class SBModule +{ +public: + + SBModule (); + + SBModule (const SBModule &rhs); + + SBModule (const SBModuleSpec &module_spec); + + const SBModule & + operator = (const SBModule &rhs); + + SBModule (lldb::SBProcess &process, + lldb::addr_t header_addr); + + ~SBModule (); + + bool + IsValid () const; + + void + Clear(); + + //------------------------------------------------------------------ + /// Get const accessor for the module file specification. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetFileSpec () const; + + //------------------------------------------------------------------ + /// Get accessor for the module platform file specification. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file '/usr/lib/liba.dylib' + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// '/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib' + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetPlatformFileSpec () const; + + bool + SetPlatformFileSpec (const lldb::SBFileSpec &platform_file); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize(); + + const char * + GetTriple (); + + const uint8_t * + GetUUIDBytes () const; + + const char * + GetUUIDString () const; + + bool + operator == (const lldb::SBModule &rhs) const; + + bool + operator != (const lldb::SBModule &rhs) const; + + lldb::SBSection + FindSection (const char *sect_name); + + lldb::SBAddress + ResolveFileAddress (lldb::addr_t vm_addr); + + lldb::SBSymbolContext + ResolveSymbolContextForAddress (const lldb::SBAddress& addr, + uint32_t resolve_scope); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetNumCompileUnits(); + + lldb::SBCompileUnit + GetCompileUnitAtIndex (uint32_t); + + size_t + GetNumSymbols (); + + lldb::SBSymbol + GetSymbolAtIndex (size_t idx); + + lldb::SBSymbol + FindSymbol (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + lldb::SBSymbolContextList + FindSymbols (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + size_t + GetNumSections (); + + lldb::SBSection + GetSectionAtIndex (size_t idx); + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @param[in] name_type_mask + /// A logical OR of one or more FunctionNameType enum bits that + /// indicate what kind of names should be used when doing the + /// lookup. Bits include fully qualified names, base names, + /// C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @return + /// A lldb::SBSymbolContextList that gets filled in with all of + /// the symbol contexts for all the matches. + //------------------------------------------------------------------ + lldb::SBSymbolContextList + FindFunctions (const char *name, + uint32_t name_type_mask = lldb::eFunctionNameTypeAny); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] target + /// A valid SBTarget instance representing the debuggee. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a max_matches. + /// + /// @return + /// A list of matched variables in an SBValueList. + //------------------------------------------------------------------ + lldb::SBValueList + FindGlobalVariables (lldb::SBTarget &target, + const char *name, + uint32_t max_matches); + + //------------------------------------------------------------------ + /// Find the first global (or static) variable by name. + /// + /// @param[in] target + /// A valid SBTarget instance representing the debuggee. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @return + /// An SBValue that gets filled in with the found variable (if any). + //------------------------------------------------------------------ + lldb::SBValue + FindFirstGlobalVariable (lldb::SBTarget &target, const char *name); + + lldb::SBType + FindFirstType (const char* name); + + lldb::SBTypeList + FindTypes (const char* type); + + lldb::SBType + GetBasicType(lldb::BasicType type); + + //------------------------------------------------------------------ + /// Get all types matching \a type_mask from debug info in this + /// module. + /// + /// @param[in] type_mask + /// A bitfield that consists of one or more bits logically OR'ed + /// together from the lldb::TypeClass enumeration. This allows + /// you to request only structure types, or only class, struct + /// and union types. Passing in lldb::eTypeClassAny will return + /// all types found in the debug information for this module. + /// + /// @return + /// A list of types in this module that match \a type_mask + //------------------------------------------------------------------ + lldb::SBTypeList + GetTypes (uint32_t type_mask = lldb::eTypeClassAny); + + //------------------------------------------------------------------ + /// Get the module version numbers. + /// + /// Many object files have a set of version numbers that describe + /// the version of the executable or shared library. Typically there + /// are major, minor and build, but there may be more. This function + /// will extract the versions from object files if they are available. + /// + /// If \a versions is NULL, or if \a num_versions is 0, the return + /// value will indicate how many version numbers are available in + /// this object file. Then a subsequent call can be made to this + /// function with a value of \a versions and \a num_versions that + /// has enough storage to store some or all version numbers. + /// + /// @param[out] versions + /// A pointer to an array of uint32_t types that is \a num_versions + /// long. If this value is NULL, the return value will indicate + /// how many version numbers are required for a subsequent call + /// to this function so that all versions can be retrieved. If + /// the value is non-NULL, then at most \a num_versions of the + /// existing versions numbers will be filled into \a versions. + /// If there is no version information available, \a versions + /// will be filled with \a num_versions UINT32_MAX values + /// and zero will be returned. + /// + /// @param[in] num_versions + /// The maximum number of entries to fill into \a versions. If + /// this value is zero, then the return value will indicate + /// how many version numbers there are in total so another call + /// to this function can be make with adequate storage in + /// \a versions to get all of the version numbers. If \a + /// num_versions is less than the actual number of version + /// numbers in this object file, only \a num_versions will be + /// filled into \a versions (if \a versions is non-NULL). + /// + /// @return + /// This function always returns the number of version numbers + /// that this object file has regardless of the number of + /// version numbers that were copied into \a versions. + //------------------------------------------------------------------ + uint32_t + GetVersion (uint32_t *versions, + uint32_t num_versions); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBSection; + friend class SBSymbolContext; + friend class SBTarget; + + explicit SBModule (const lldb::ModuleSP& module_sp); + + ModuleSP + GetSP () const; + + void + SetSP (const ModuleSP &module_sp); + + lldb::ModuleSP m_opaque_sp; +}; + + +} // namespace lldb + +#endif // LLDB_SBModule_h_ diff --git a/include/lldb/API/SBModuleSpec.h b/include/lldb/API/SBModuleSpec.h new file mode 100644 index 00000000000..a615e017cbc --- /dev/null +++ b/include/lldb/API/SBModuleSpec.h @@ -0,0 +1,154 @@ +//===-- SBModuleSpec.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBModuleSpec_h_ +#define LLDB_SBModuleSpec_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBFileSpec.h" + +namespace lldb { + +class SBModuleSpec +{ +public: + + SBModuleSpec (); + + SBModuleSpec (const SBModuleSpec &rhs); + + ~SBModuleSpec (); + + const SBModuleSpec & + operator = (const SBModuleSpec &rhs); + + bool + IsValid () const; + + void + Clear(); + + //------------------------------------------------------------------ + /// Get const accessor for the module file. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetFileSpec (); + + void + SetFileSpec (const lldb::SBFileSpec &fspec); + + //------------------------------------------------------------------ + /// Get accessor for the module platform file. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file '/usr/lib/liba.dylib' + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// '/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib' + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + lldb::SBFileSpec + GetPlatformFileSpec (); + + void + SetPlatformFileSpec (const lldb::SBFileSpec &fspec); + + lldb::SBFileSpec + GetSymbolFileSpec (); + + void + SetSymbolFileSpec (const lldb::SBFileSpec &fspec); + + const char * + GetObjectName (); + + void + SetObjectName (const char *name); + + const char * + GetTriple (); + + void + SetTriple (const char *triple); + + const uint8_t * + GetUUIDBytes (); + + size_t + GetUUIDLength (); + + bool + SetUUIDBytes (const uint8_t *uuid, size_t uuid_len); + + bool + GetDescription (lldb::SBStream &description); + +private: + friend class SBModuleSpecList; + friend class SBModule; + friend class SBTarget; + + std::unique_ptr m_opaque_ap; +}; + +class SBModuleSpecList +{ +public: + SBModuleSpecList(); + + SBModuleSpecList (const SBModuleSpecList &rhs); + + ~SBModuleSpecList(); + + SBModuleSpecList & + operator = (const SBModuleSpecList &rhs); + + static SBModuleSpecList + GetModuleSpecifications (const char *path); + + void + Append (const SBModuleSpec &spec); + + void + Append (const SBModuleSpecList &spec_list); + + SBModuleSpec + FindFirstMatchingSpec (const SBModuleSpec &match_spec); + + SBModuleSpecList + FindMatchingSpecs (const SBModuleSpec &match_spec); + + size_t + GetSize(); + + SBModuleSpec + GetSpecAtIndex (size_t i); + + bool + GetDescription (lldb::SBStream &description); + +private: + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBModuleSpec_h_ diff --git a/include/lldb/API/SBProcess.h b/include/lldb/API/SBProcess.h new file mode 100644 index 00000000000..784f362122a --- /dev/null +++ b/include/lldb/API/SBProcess.h @@ -0,0 +1,295 @@ +//===-- SBProcess.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBProcess_h_ +#define LLDB_SBProcess_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBTarget.h" +#include + +namespace lldb { + +class SBEvent; + +class SBProcess +{ +public: + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStateChanged = (1 << 0), + eBroadcastBitInterrupt = (1 << 1), + eBroadcastBitSTDOUT = (1 << 2), + eBroadcastBitSTDERR = (1 << 3), + eBroadcastBitProfileData = (1 << 4) + }; + + SBProcess (); + + SBProcess (const lldb::SBProcess& rhs); + + const lldb::SBProcess& + operator = (const lldb::SBProcess& rhs); + + SBProcess (const lldb::ProcessSP &process_sp); + + ~SBProcess(); + + static const char * + GetBroadcasterClassName (); + + const char * + GetPluginName (); + + // DEPRECATED: use GetPluginName() + const char * + GetShortPluginName (); + + void + Clear (); + + bool + IsValid() const; + + lldb::SBTarget + GetTarget() const; + + lldb::ByteOrder + GetByteOrder() const; + + size_t + PutSTDIN (const char *src, size_t src_len); + + size_t + GetSTDOUT (char *dst, size_t dst_len) const; + + size_t + GetSTDERR (char *dst, size_t dst_len) const; + + size_t + GetAsyncProfileData(char *dst, size_t dst_len) const; + + void + ReportEventState (const lldb::SBEvent &event, FILE *out) const; + + void + AppendEventStateReport (const lldb::SBEvent &event, lldb::SBCommandReturnObject &result); + + //------------------------------------------------------------------ + /// Remote connection related functions. These will fail if the + /// process is not in eStateConnected. They are intended for use + /// when connecting to an externally managed debugserver instance. + //------------------------------------------------------------------ + bool + RemoteAttachToProcessWithID (lldb::pid_t pid, + lldb::SBError& error); + + bool + 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); + + //------------------------------------------------------------------ + // Thread related functions + //------------------------------------------------------------------ + uint32_t + GetNumThreads (); + + lldb::SBThread + GetThreadAtIndex (size_t index); + + lldb::SBThread + GetThreadByID (lldb::tid_t sb_thread_id); + + lldb::SBThread + GetThreadByIndexID (uint32_t index_id); + + lldb::SBThread + GetSelectedThread () const; + + //------------------------------------------------------------------ + // Function for lazily creating a thread using the current OS + // plug-in. This function will be removed in the future when there + // are APIs to create SBThread objects through the interface and add + // them to the process through the SBProcess API. + //------------------------------------------------------------------ + lldb::SBThread + CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); + + bool + SetSelectedThread (const lldb::SBThread &thread); + + bool + SetSelectedThreadByID (lldb::tid_t tid); + + bool + SetSelectedThreadByIndexID (uint32_t index_id); + + //------------------------------------------------------------------ + // Stepping related functions + //------------------------------------------------------------------ + + lldb::StateType + GetState (); + + int + GetExitStatus (); + + const char * + GetExitDescription (); + + //------------------------------------------------------------------ + /// Gets the process ID + /// + /// Returns the process identifier for the process as it is known + /// on the system on which the process is running. For unix systems + /// this is typically the same as if you called "getpid()" in the + /// process. + /// + /// @return + /// Returns LLDB_INVALID_PROCESS_ID if this object does not + /// contain a valid process object, or if the process has not + /// been launched. Returns a valid process ID if the process is + /// valid. + //------------------------------------------------------------------ + lldb::pid_t + GetProcessID (); + + //------------------------------------------------------------------ + /// Gets the unique ID associated with this process object + /// + /// Unique IDs start at 1 and increment up with each new process + /// instance. Since starting a process on a system might always + /// create a process with the same process ID, there needs to be a + /// way to tell two process instances apart. + /// + /// @return + /// Returns a non-zero integer ID if this object contains a + /// valid process object, zero if this object does not contain + /// a valid process object. + //------------------------------------------------------------------ + uint32_t + GetUniqueID(); + + uint32_t + GetAddressByteSize() const; + + lldb::SBError + Destroy (); + + lldb::SBError + Continue (); + + lldb::SBError + Stop (); + + lldb::SBError + Kill (); + + lldb::SBError + Detach (); + + lldb::SBError + Detach (bool keep_stopped); + + lldb::SBError + Signal (int signal); + + void + SendAsyncInterrupt(); + + uint32_t + GetStopID(bool include_expression_stops = false); + + size_t + ReadMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); + + size_t + WriteMemory (addr_t addr, const void *buf, size_t size, lldb::SBError &error); + + size_t + ReadCStringFromMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); + + uint64_t + ReadUnsignedFromMemory (addr_t addr, uint32_t byte_size, lldb::SBError &error); + + lldb::addr_t + ReadPointerFromMemory (addr_t addr, lldb::SBError &error); + + // Events + static lldb::StateType + GetStateFromEvent (const lldb::SBEvent &event); + + static bool + GetRestartedFromEvent (const lldb::SBEvent &event); + + static size_t + GetNumRestartedReasonsFromEvent (const lldb::SBEvent &event); + + static const char * + GetRestartedReasonAtIndexFromEvent (const lldb::SBEvent &event, size_t idx); + + static lldb::SBProcess + GetProcessFromEvent (const lldb::SBEvent &event); + + static bool + EventIsProcessEvent (const lldb::SBEvent &event); + + lldb::SBBroadcaster + GetBroadcaster () const; + + static const char * + GetBroadcasterClass (); + + bool + GetDescription (lldb::SBStream &description); + + uint32_t + GetNumSupportedHardwareWatchpoints (lldb::SBError &error) const; + + uint32_t + LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error); + + lldb::SBError + UnloadImage (uint32_t image_token); + +protected: + friend class SBAddress; + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBCommandInterpreter; + friend class SBDebugger; + friend class SBFunction; + friend class SBModule; + friend class SBTarget; + friend class SBThread; + friend class SBValue; + + lldb::ProcessSP + GetSP() const; + + void + SetSP (const lldb::ProcessSP &process_sp); + + lldb::ProcessWP m_opaque_wp; +}; + +} // namespace lldb + +#endif // LLDB_SBProcess_h_ diff --git a/include/lldb/API/SBSection.h b/include/lldb/API/SBSection.h new file mode 100644 index 00000000000..3386484f649 --- /dev/null +++ b/include/lldb/API/SBSection.h @@ -0,0 +1,104 @@ +//===-- SBSection.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSection_h_ +#define LLDB_SBSection_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBData.h" + +namespace lldb { + +class SBSection +{ +public: + + SBSection (); + + SBSection (const lldb::SBSection &rhs); + + ~SBSection (); + + const lldb::SBSection & + operator = (const lldb::SBSection &rhs); + + bool + IsValid () const; + + const char * + GetName (); + + lldb::SBSection + GetParent(); + + lldb::SBSection + FindSubSection (const char *sect_name); + + size_t + GetNumSubSections (); + + lldb::SBSection + GetSubSectionAtIndex (size_t idx); + + lldb::addr_t + GetFileAddress (); + + lldb::addr_t + GetLoadAddress (lldb::SBTarget &target); + + lldb::addr_t + GetByteSize (); + + uint64_t + GetFileOffset (); + + uint64_t + GetFileByteSize (); + + lldb::SBData + GetSectionData (); + + lldb::SBData + GetSectionData (uint64_t offset, + uint64_t size); + + SectionType + GetSectionType (); + + bool + operator == (const lldb::SBSection &rhs); + + bool + operator != (const lldb::SBSection &rhs); + + bool + GetDescription (lldb::SBStream &description); + + +private: + + friend class SBAddress; + friend class SBModule; + friend class SBTarget; + + SBSection (const lldb::SectionSP §ion_sp); + + lldb::SectionSP + GetSP() const; + + void + SetSP(const lldb::SectionSP §ion_sp); + + lldb::SectionWP m_opaque_wp; +}; + + +} // namespace lldb + +#endif // LLDB_SBSection_h_ diff --git a/include/lldb/API/SBSourceManager.h b/include/lldb/API/SBSourceManager.h new file mode 100644 index 00000000000..5b52c49ff3e --- /dev/null +++ b/include/lldb/API/SBSourceManager.h @@ -0,0 +1,53 @@ +//===-- SBSourceManager.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSourceManager_h_ +#define LLDB_SBSourceManager_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +class SBSourceManager +{ +public: + SBSourceManager (const SBDebugger &debugger); + SBSourceManager (const SBTarget &target); + SBSourceManager (const SBSourceManager &rhs); + + ~SBSourceManager(); + + const lldb::SBSourceManager & + operator = (const lldb::SBSourceManager &rhs); + + size_t + DisplaySourceLinesWithLineNumbers (const lldb::SBFileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + lldb::SBStream &s); + + +protected: + friend class SBCommandInterpreter; + friend class SBDebugger; + + SBSourceManager(lldb_private::SourceManager *source_manager); + +private: + + std::unique_ptr m_opaque_ap; +}; + +} // namespace lldb + +#endif // LLDB_SBSourceManager_h_ diff --git a/include/lldb/API/SBStream.h b/include/lldb/API/SBStream.h new file mode 100644 index 00000000000..038adf68542 --- /dev/null +++ b/include/lldb/API/SBStream.h @@ -0,0 +1,111 @@ +//===-- SBStream.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBStream_h_ +#define LLDB_SBStream_h_ + +#include + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBStream +{ +public: + + SBStream (); + + ~SBStream (); + + bool + IsValid() const; + + // 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 * + 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 + GetSize(); + + void + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + RedirectToFile (const char *path, bool append); + + void + RedirectToFileHandle (FILE *fh, bool transfer_fh_ownership); + + void + RedirectToFileDescriptor (int fd, bool transfer_fh_ownership); + + // If the stream is redirected to a file, forget about the file and if + // ownership of the file was transfered to this object, close the file. + // If the stream is backed by a local cache, clear this cache. + void + Clear (); + +protected: + friend class SBAddress; + friend class SBBlock; + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBCommandReturnObject; + friend class SBCompileUnit; + friend class SBData; + friend class SBDebugger; + friend class SBDeclaration; + friend class SBEvent; + friend class SBFileSpec; + friend class SBFileSpecList; + friend class SBFrame; + friend class SBFunction; + friend class SBInstruction; + friend class SBInstructionList; + friend class SBLineEntry; + friend class SBModule; + friend class SBModuleSpec; + friend class SBModuleSpecList; + friend class SBProcess; + friend class SBSection; + friend class SBSourceManager; + friend class SBSymbol; + friend class SBSymbolContext; + friend class SBSymbolContextList; + friend class SBTarget; + friend class SBThread; + friend class SBType; + friend class SBTypeMember; + friend class SBValue; + friend class SBWatchpoint; + + lldb_private::Stream * + operator->(); + + lldb_private::Stream * + get(); + + lldb_private::Stream & + ref(); + +private: + + DISALLOW_COPY_AND_ASSIGN (SBStream); + std::unique_ptr m_opaque_ap; + bool m_is_file; +}; + +} // namespace lldb + +#endif // LLDB_SBStream_h_ diff --git a/include/lldb/API/SBStringList.h b/include/lldb/API/SBStringList.h new file mode 100644 index 00000000000..9d0be6e8a74 --- /dev/null +++ b/include/lldb/API/SBStringList.h @@ -0,0 +1,71 @@ +//===-- SBStringList.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBStringList_h_ +#define LLDB_SBStringList_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBStringList +{ +public: + + SBStringList (); + + SBStringList (const lldb::SBStringList &rhs); + + const SBStringList & + operator = (const SBStringList &rhs); + + ~SBStringList (); + + bool + IsValid() const; + + void + AppendString (const char *str); + + void + AppendList (const char **strv, int strc); + + void + AppendList (const lldb::SBStringList &strings); + + uint32_t + GetSize () const; + + const char * + GetStringAtIndex (size_t idx); + + void + Clear (); + +protected: + friend class SBCommandInterpreter; + friend class SBDebugger; + + SBStringList (const lldb_private::StringList *lldb_strings); + + const lldb_private::StringList * + operator->() const; + + const lldb_private::StringList & + operator*() const; + +private: + + std::unique_ptr m_opaque_ap; + +}; + +} // namespace lldb + +#endif // LLDB_SBStringList_h_ diff --git a/include/lldb/API/SBSymbol.h b/include/lldb/API/SBSymbol.h new file mode 100644 index 00000000000..0a528a9ac96 --- /dev/null +++ b/include/lldb/API/SBSymbol.h @@ -0,0 +1,109 @@ +//===-- SBSymbol.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSymbol_h_ +#define LLDB_SBSymbol_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBTarget.h" + +namespace lldb { + +class SBSymbol +{ +public: + + SBSymbol (); + + ~SBSymbol (); + + SBSymbol (const lldb::SBSymbol &rhs); + + const lldb::SBSymbol & + operator = (const lldb::SBSymbol &rhs); + + bool + IsValid () const; + + + const char * + GetName() const; + + const char * + GetMangledName () const; + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target); + + lldb::SBInstructionList + GetInstructions (lldb::SBTarget target, const char *flavor_string); + + SBAddress + GetStartAddress (); + + SBAddress + GetEndAddress (); + + uint32_t + GetPrologueByteSize (); + + SymbolType + GetType (); + + bool + operator == (const lldb::SBSymbol &rhs) const; + + bool + operator != (const lldb::SBSymbol &rhs) const; + + bool + GetDescription (lldb::SBStream &description); + + //---------------------------------------------------------------------- + // Returns true if the symbol is externally visible in the module that + // it is defined in + //---------------------------------------------------------------------- + bool + IsExternal(); + + //---------------------------------------------------------------------- + // Returns true if the symbol was synthetically generated from something + // other than the actual symbol table itself in the object file. + //---------------------------------------------------------------------- + bool + IsSynthetic(); + +protected: + + lldb_private::Symbol * + get (); + + void + reset (lldb_private::Symbol *); + +private: + friend class SBAddress; + friend class SBFrame; + friend class SBModule; + friend class SBSymbolContext; + + SBSymbol (lldb_private::Symbol *lldb_object_ptr); + + void + SetSymbol (lldb_private::Symbol *lldb_object_ptr); + + lldb_private::Symbol *m_opaque_ptr; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbol_h_ diff --git a/include/lldb/API/SBSymbolContext.h b/include/lldb/API/SBSymbolContext.h new file mode 100644 index 00000000000..fee2d19179d --- /dev/null +++ b/include/lldb/API/SBSymbolContext.h @@ -0,0 +1,94 @@ +//===-- SBSymbolContext.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSymbolContext_h_ +#define LLDB_SBSymbolContext_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBBlock.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBModule.h" +#include "lldb/API/SBSymbol.h" + +namespace lldb { + +class SBSymbolContext +{ +public: + SBSymbolContext (); + + SBSymbolContext (const lldb::SBSymbolContext& rhs); + + ~SBSymbolContext (); + + bool + IsValid () const; + + const lldb::SBSymbolContext & + operator = (const lldb::SBSymbolContext &rhs); + + lldb::SBModule GetModule (); + lldb::SBCompileUnit GetCompileUnit (); + lldb::SBFunction GetFunction (); + lldb::SBBlock GetBlock (); + lldb::SBLineEntry GetLineEntry (); + lldb::SBSymbol GetSymbol (); + + void SetModule (lldb::SBModule module); + void SetCompileUnit (lldb::SBCompileUnit compile_unit); + void SetFunction (lldb::SBFunction function); + void SetBlock (lldb::SBBlock block); + void SetLineEntry (lldb::SBLineEntry line_entry); + void SetSymbol (lldb::SBSymbol symbol); + + SBSymbolContext + GetParentOfInlinedScope (const SBAddress &curr_frame_pc, + SBAddress &parent_frame_addr) const; + + bool + GetDescription (lldb::SBStream &description); + +protected: + friend class SBAddress; + friend class SBFrame; + friend class SBModule; + friend class SBThread; + friend class SBTarget; + friend class SBSymbolContextList; + + lldb_private::SymbolContext* + operator->() const; + + lldb_private::SymbolContext& + operator*(); + + lldb_private::SymbolContext& + ref(); + + const lldb_private::SymbolContext& + operator*() const; + + lldb_private::SymbolContext * + get() const; + + SBSymbolContext (const lldb_private::SymbolContext *sc_ptr); + + void + SetSymbolContext (const lldb_private::SymbolContext *sc_ptr); + +private: + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbolContext_h_ diff --git a/include/lldb/API/SBSymbolContextList.h b/include/lldb/API/SBSymbolContextList.h new file mode 100644 index 00000000000..6cc78e472c6 --- /dev/null +++ b/include/lldb/API/SBSymbolContextList.h @@ -0,0 +1,69 @@ +//===-- SBSymbolContextList.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBSymbolContextList_h_ +#define LLDB_SBSymbolContextList_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBSymbolContext.h" + +namespace lldb { + +class SBSymbolContextList +{ +public: + SBSymbolContextList (); + + SBSymbolContextList (const lldb::SBSymbolContextList& rhs); + + ~SBSymbolContextList (); + + const lldb::SBSymbolContextList & + operator = (const lldb::SBSymbolContextList &rhs); + + bool + IsValid () const; + + uint32_t + GetSize() const; + + lldb::SBSymbolContext + GetContextAtIndex (uint32_t idx); + + bool + GetDescription (lldb::SBStream &description); + + void + Append (lldb::SBSymbolContext &sc); + + void + Append (lldb::SBSymbolContextList &sc_list); + + void + Clear(); + +protected: + + friend class SBModule; + friend class SBTarget; + + lldb_private::SymbolContextList* + operator->() const; + + lldb_private::SymbolContextList& + operator*() const; + +private: + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBSymbolContextList_h_ diff --git a/include/lldb/API/SBTarget.h b/include/lldb/API/SBTarget.h new file mode 100644 index 00000000000..15aeed4b600 --- /dev/null +++ b/include/lldb/API/SBTarget.h @@ -0,0 +1,828 @@ +//===-- SBTarget.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTarget_h_ +#define LLDB_SBTarget_h_ + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFileSpecList.h" +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBWatchpoint.h" + +namespace lldb { + +class SBLaunchInfo +{ +public: + SBLaunchInfo (const char **argv); + + ~SBLaunchInfo(); + + uint32_t + GetUserID(); + + uint32_t + GetGroupID(); + + bool + UserIDIsValid (); + + bool + GroupIDIsValid (); + + void + SetUserID (uint32_t uid); + + void + SetGroupID (uint32_t gid); + + uint32_t + GetNumArguments (); + + const char * + GetArgumentAtIndex (uint32_t idx); + + void + SetArguments (const char **argv, bool append); + + uint32_t + GetNumEnvironmentEntries (); + + const char * + GetEnvironmentEntryAtIndex (uint32_t idx); + + void + SetEnvironmentEntries (const char **envp, bool append); + + void + Clear (); + + const char * + GetWorkingDirectory () const; + + void + SetWorkingDirectory (const char *working_dir); + + uint32_t + GetLaunchFlags (); + + void + SetLaunchFlags (uint32_t flags); + + const char * + GetProcessPluginName (); + + void + SetProcessPluginName (const char *plugin_name); + + const char * + GetShell (); + + void + SetShell (const char * path); + + uint32_t + GetResumeCount (); + + void + SetResumeCount (uint32_t c); + + bool + AddCloseFileAction (int fd); + + bool + AddDuplicateFileAction (int fd, int dup_fd); + + bool + AddOpenFileAction (int fd, const char *path, bool read, bool write); + + bool + AddSuppressFileAction (int fd, bool read, bool write); + +protected: + friend class SBTarget; + + lldb_private::ProcessLaunchInfo & + ref (); + + ProcessLaunchInfoSP m_opaque_sp; +}; + +class SBAttachInfo +{ +public: + SBAttachInfo (); + + SBAttachInfo (lldb::pid_t pid); + + SBAttachInfo (const char *path, bool wait_for); + + SBAttachInfo (const SBAttachInfo &rhs); + + ~SBAttachInfo(); + + SBAttachInfo & + operator = (const SBAttachInfo &rhs); + + lldb::pid_t + GetProcessID (); + + void + SetProcessID (lldb::pid_t pid); + + void + SetExecutable (const char *path); + + void + SetExecutable (lldb::SBFileSpec exe_file); + + bool + GetWaitForLaunch (); + + void + SetWaitForLaunch (bool b); + + bool + GetIgnoreExisting (); + + void + SetIgnoreExisting (bool b); + + uint32_t + GetResumeCount (); + + void + SetResumeCount (uint32_t c); + + const char * + GetProcessPluginName (); + + void + SetProcessPluginName (const char *plugin_name); + + uint32_t + GetUserID(); + + uint32_t + GetGroupID(); + + bool + UserIDIsValid (); + + bool + GroupIDIsValid (); + + void + SetUserID (uint32_t uid); + + void + SetGroupID (uint32_t gid); + + uint32_t + GetEffectiveUserID(); + + uint32_t + GetEffectiveGroupID(); + + bool + EffectiveUserIDIsValid (); + + bool + EffectiveGroupIDIsValid (); + + void + SetEffectiveUserID (uint32_t uid); + + void + SetEffectiveGroupID (uint32_t gid); + + lldb::pid_t + GetParentProcessID (); + + void + SetParentProcessID (lldb::pid_t pid); + + bool + ParentProcessIDIsValid(); + + +protected: + friend class SBTarget; + + lldb_private::ProcessAttachInfo & + ref (); + + ProcessAttachInfoSP m_opaque_sp; +}; + +class SBTarget +{ +public: + //------------------------------------------------------------------ + // Broadcaster bits. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2), + eBroadcastBitWatchpointChanged = (1 << 3), + eBroadcastBitSymbolsLoaded = (1 << 4) + }; + + //------------------------------------------------------------------ + // Constructors + //------------------------------------------------------------------ + SBTarget (); + + SBTarget (const lldb::SBTarget& rhs); + + SBTarget (const lldb::TargetSP& target_sp); + + const lldb::SBTarget& + operator = (const lldb::SBTarget& rhs); + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~SBTarget(); + + bool + IsValid() const; + + static const char * + GetBroadcasterClassName (); + + lldb::SBProcess + GetProcess (); + + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using the + /// target object's executable module's file as the file to launch. + /// Arguments are given in \a argv, and the environment variables + /// are in \a envp. Standard input and output files can be + /// optionally re-directed to \a stdin_path, \a stdout_path, and + /// \a stderr_path. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @param[in] launch_flags + /// Some launch options specified by logical OR'ing + /// lldb::LaunchFlags enumeration values together. + /// + /// @param[in] stop_at_endtry + /// If false do not stop the inferior at the entry point. + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// A process object for the newly created process. + //------------------------------------------------------------------ + lldb::SBProcess + 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); + + + //------------------------------------------------------------------ + /// Launch a new process with sensible defaults. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// Default: listener + /// Set to the target's debugger (SBTarget::GetDebugger()) + /// + /// Default: launch_flags + /// Empty launch flags + /// + /// Default: stdin_path + /// Default: stdout_path + /// Default: stderr_path + /// A pseudo terminal will be used. + /// + /// @return + /// A process object for the newly created process. + //------------------------------------------------------------------ + SBProcess + LaunchSimple (const char **argv, + const char **envp, + const char *working_directory); + + SBProcess + Launch (SBLaunchInfo &launch_info, SBError& error); + + SBProcess + LoadCore (const char *core_file); + + SBProcess + Attach (SBAttachInfo &attach_info, SBError& error); + + //------------------------------------------------------------------ + /// Attach to process with pid. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] pid + /// The process ID to attach to. + /// + /// @param[out] + /// An error explaining what went wrong if attach fails. + /// + /// @return + /// A process object for the attached process. + //------------------------------------------------------------------ + lldb::SBProcess + AttachToProcessWithID (SBListener &listener, + lldb::pid_t pid, + lldb::SBError& error); + +#if defined(__APPLE__) + // We need to keep this around for a build or two since Xcode links + // to the 32 bit version of this function. We will take it out soon. + lldb::SBProcess + AttachToProcessWithID (SBListener &listener, + ::pid_t pid, // 32 bit int process ID + lldb::SBError& error); // DEPRECATED +#endif + //------------------------------------------------------------------ + /// Attach to process with name. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] name + /// Basename of process to attach to. + /// + /// @param[in] wait_for + /// If true wait for a new instance of 'name' to be launched. + /// + /// @param[out] + /// An error explaining what went wrong if attach fails. + /// + /// @return + /// A process object for the attached process. + //------------------------------------------------------------------ + lldb::SBProcess + AttachToProcessWithName (SBListener &listener, + const char *name, + bool wait_for, + lldb::SBError& error); + + //------------------------------------------------------------------ + /// Connect to a remote debug server with url. + /// + /// @param[in] listener + /// An optional listener that will receive all process events. + /// If \a listener is valid then \a listener will listen to all + /// process events. If not valid, then this target's debugger + /// (SBTarget::GetDebugger()) will listen to all process events. + /// + /// @param[in] url + /// The url to connect to, e.g., 'connect://localhost:12345'. + /// + /// @param[in] plugin_name + /// The plugin name to be used; can be NULL. + /// + /// @param[out] + /// An error explaining what went wrong if the connect fails. + /// + /// @return + /// A process object for the connected process. + //------------------------------------------------------------------ + lldb::SBProcess + ConnectRemote (SBListener &listener, + const char *url, + const char *plugin_name, + SBError& error); + + lldb::SBFileSpec + GetExecutable (); + + bool + AddModule (lldb::SBModule &module); + + lldb::SBModule + AddModule (const char *path, + const char *triple, + const char *uuid); + + lldb::SBModule + AddModule (const char *path, + const char *triple, + const char *uuid_cstr, + const char *symfile); + + lldb::SBModule + AddModule (const SBModuleSpec &module_spec); + + uint32_t + GetNumModules () const; + + lldb::SBModule + GetModuleAtIndex (uint32_t idx); + + bool + RemoveModule (lldb::SBModule module); + + lldb::SBDebugger + GetDebugger() const; + + lldb::SBModule + FindModule (const lldb::SBFileSpec &file_spec); + + lldb::ByteOrder + GetByteOrder (); + + uint32_t + GetAddressByteSize(); + + const char * + GetTriple (); + + //------------------------------------------------------------------ + /// Set the base load address for a module section. + /// + /// @param[in] section + /// The section whose base load address will be set within this + /// target. + /// + /// @param[in] section_base_addr + /// The base address for the section. + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + SetSectionLoadAddress (lldb::SBSection section, + lldb::addr_t section_base_addr); + + //------------------------------------------------------------------ + /// Clear the base load address for a module section. + /// + /// @param[in] section + /// The section whose base load address will be cleared within + /// this target. + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + ClearSectionLoadAddress (lldb::SBSection section); + + //------------------------------------------------------------------ + /// Slide all file addresses for all module sections so that \a module + /// appears to loaded at these slide addresses. + /// + /// When you need all sections within a module to be loaded at a + /// rigid slide from the addresses found in the module object file, + /// this function will allow you to easily and quickly slide all + /// module sections. + /// + /// @param[in] module + /// The module to load. + /// + /// @param[in] sections_offset + /// An offset that will be applied to all section file addresses + /// (the virtual addresses found in the object file itself). + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + SetModuleLoadAddress (lldb::SBModule module, + int64_t sections_offset); + + + //------------------------------------------------------------------ + /// The the section base load addresses for all sections in a module. + /// + /// @param[in] module + /// The module to unload. + /// + /// @return + /// An error to indicate success, fail, and any reason for + /// failure. + //------------------------------------------------------------------ + lldb::SBError + ClearModuleLoadAddress (lldb::SBModule module); + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// @param[in] name + /// The name of the function we are looking for. + /// + /// @param[in] name_type_mask + /// A logical OR of one or more FunctionNameType enum bits that + /// indicate what kind of names should be used when doing the + /// lookup. Bits include fully qualified names, base names, + /// C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @return + /// A lldb::SBSymbolContextList that gets filled in with all of + /// the symbol contexts for all the matches. + //------------------------------------------------------------------ + lldb::SBSymbolContextList + FindFunctions (const char *name, + uint32_t name_type_mask = lldb::eFunctionNameTypeAny); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a max_matches. + /// + /// @return + /// A list of matched variables in an SBValueList. + //------------------------------------------------------------------ + lldb::SBValueList + FindGlobalVariables (const char *name, + uint32_t max_matches); + + //------------------------------------------------------------------ + /// Find the first global (or static) variable by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @return + /// An SBValue that gets filled in with the found variable (if any). + //------------------------------------------------------------------ + lldb::SBValue + FindFirstGlobalVariable (const char* name); + + void + Clear (); + + lldb::SBAddress + ResolveLoadAddress (lldb::addr_t vm_addr); + + SBSymbolContext + ResolveSymbolContextForAddress (const SBAddress& addr, + uint32_t resolve_scope); + + lldb::SBBreakpoint + BreakpointCreateByLocation (const char *file, uint32_t line); + + lldb::SBBreakpoint + BreakpointCreateByLocation (const lldb::SBFileSpec &file_spec, uint32_t line); + + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, const char *module_name = NULL); + + // This version uses name_type_mask = eFunctionNameTypeAuto + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByName (const char *symbol_name, + uint32_t name_type_mask, // Logical OR one or more FunctionNameType enum bits + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByNames (const char *symbol_name[], + uint32_t num_names, + uint32_t name_type_mask, // Logical OR one or more FunctionNameType enum bits + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateByRegex (const char *symbol_name_regex, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list); + + lldb::SBBreakpoint + BreakpointCreateBySourceRegex (const char *source_regex, + const lldb::SBFileSpec &source_file, + const char *module_name = NULL); + + lldb::SBBreakpoint + BreakpointCreateBySourceRegex (const char *source_regex, + const SBFileSpecList &module_list, + const lldb::SBFileSpecList &source_file); + + lldb::SBBreakpoint + BreakpointCreateForException (lldb::LanguageType language, + bool catch_bp, + bool throw_bp); + + lldb::SBBreakpoint + BreakpointCreateByAddress (addr_t address); + + uint32_t + GetNumBreakpoints () const; + + lldb::SBBreakpoint + GetBreakpointAtIndex (uint32_t idx) const; + + bool + BreakpointDelete (break_id_t break_id); + + lldb::SBBreakpoint + FindBreakpointByID (break_id_t break_id); + + bool + EnableAllBreakpoints (); + + bool + DisableAllBreakpoints (); + + bool + DeleteAllBreakpoints (); + + uint32_t + GetNumWatchpoints () const; + + lldb::SBWatchpoint + GetWatchpointAtIndex (uint32_t idx) const; + + bool + DeleteWatchpoint (lldb::watch_id_t watch_id); + + lldb::SBWatchpoint + FindWatchpointByID (lldb::watch_id_t watch_id); + + lldb::SBWatchpoint + WatchAddress (lldb::addr_t addr, size_t size, bool read, bool write, SBError& error); + + bool + EnableAllWatchpoints (); + + bool + DisableAllWatchpoints (); + + bool + DeleteAllWatchpoints (); + + lldb::SBBroadcaster + GetBroadcaster () const; + + lldb::SBType + FindFirstType (const char* type); + + lldb::SBTypeList + FindTypes (const char* type); + + lldb::SBType + GetBasicType(lldb::BasicType type); + + SBSourceManager + GetSourceManager(); + + lldb::SBInstructionList + ReadInstructions (lldb::SBAddress base_addr, uint32_t count); + + lldb::SBInstructionList + ReadInstructions (lldb::SBAddress base_addr, uint32_t count, const char *flavor_string); + + lldb::SBInstructionList + GetInstructions (lldb::SBAddress base_addr, const void *buf, size_t size); + + // The "WithFlavor" is necessary to keep SWIG from getting confused about overloaded arguments when + // using the buf + size -> Python Object magic. + + lldb::SBInstructionList + GetInstructionsWithFlavor (lldb::SBAddress base_addr, const char *flavor_string, const void *buf, size_t size); + + lldb::SBInstructionList + GetInstructions (lldb::addr_t base_addr, const void *buf, size_t size); + + lldb::SBInstructionList + GetInstructionsWithFlavor (lldb::addr_t base_addr, const char *flavor_string, const void *buf, size_t size); + + lldb::SBSymbolContextList + FindSymbols (const char *name, + lldb::SymbolType type = eSymbolTypeAny); + + bool + operator == (const lldb::SBTarget &rhs) const; + + bool + operator != (const lldb::SBTarget &rhs) const; + + bool + GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level); + + lldb::SBValue + EvaluateExpression (const char *expr, const SBExpressionOptions &options); + + lldb::addr_t + GetStackRedZoneSize(); + +protected: + friend class SBAddress; + friend class SBBlock; + friend class SBDebugger; + friend class SBFunction; + friend class SBInstruction; + friend class SBModule; + friend class SBProcess; + friend class SBSection; + friend class SBSourceManager; + friend class SBSymbol; + friend class SBValue; + + //------------------------------------------------------------------ + // Constructors are private, use static Target::Create function to + // create an instance of this class. + //------------------------------------------------------------------ + + lldb::TargetSP + GetSP () const; + + void + SetSP (const lldb::TargetSP& target_sp); + + +private: + //------------------------------------------------------------------ + // For Target only + //------------------------------------------------------------------ + + lldb::TargetSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBTarget_h_ diff --git a/include/lldb/API/SBThread.h b/include/lldb/API/SBThread.h new file mode 100644 index 00000000000..9645f925035 --- /dev/null +++ b/include/lldb/API/SBThread.h @@ -0,0 +1,220 @@ +//===-- SBThread.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBThread_h_ +#define LLDB_SBThread_h_ + +#include "lldb/API/SBDefines.h" + +#include + +namespace lldb { + +class SBFrame; + +class SBThread +{ +public: + enum + { + eBroadcastBitStackChanged = (1 << 0), + eBroadcastBitThreadSuspended = (1 << 1), + eBroadcastBitThreadResumed = (1 << 2), + eBroadcastBitSelectedFrameChanged = (1 << 3), + eBroadcastBitThreadSelected = (1 << 4) + }; + + static const char * + GetBroadcasterClassName (); + + SBThread (); + + SBThread (const lldb::SBThread &thread); + + SBThread (const lldb::ThreadSP& lldb_object_sp); + + ~SBThread(); + + bool + IsValid() const; + + void + Clear (); + + lldb::StopReason + GetStopReason(); + + /// Get the number of words associated with the stop reason. + /// See also GetStopReasonDataAtIndex(). + size_t + GetStopReasonDataCount(); + + //-------------------------------------------------------------------------- + /// Get information associated with a stop reason. + /// + /// Breakpoint stop reasons will have data that consists of pairs of + /// breakpoint IDs followed by the breakpoint location IDs (they always come + /// in pairs). + /// + /// Stop Reason Count Data Type + /// ======================== ===== ========================================= + /// eStopReasonNone 0 + /// eStopReasonTrace 0 + /// eStopReasonBreakpoint N duple: {breakpoint id, location id} + /// eStopReasonWatchpoint 1 watchpoint id + /// eStopReasonSignal 1 unix signal number + /// eStopReasonException N exception data + /// eStopReasonExec 0 + /// eStopReasonPlanComplete 0 + //-------------------------------------------------------------------------- + uint64_t + GetStopReasonDataAtIndex(uint32_t idx); + + size_t + GetStopDescription (char *dst, size_t dst_len); + + SBValue + GetStopReturnValue (); + + lldb::tid_t + GetThreadID () const; + + uint32_t + GetIndexID () const; + + const char * + GetName () const; + + const char * + GetQueueName() const; + + void + StepOver (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepInto (const char *target_name, lldb::RunMode stop_other_threads = lldb::eOnlyDuringStepping); + + void + StepOut (); + + void + StepOutOfFrame (lldb::SBFrame &frame); + + void + StepInstruction(bool step_over); + + SBError + StepOverUntil (lldb::SBFrame &frame, + lldb::SBFileSpec &file_spec, + uint32_t line); + + void + RunToAddress (lldb::addr_t addr); + + SBError + ReturnFromFrame (SBFrame &frame, SBValue &return_value); + + //-------------------------------------------------------------------------- + /// LLDB currently supports process centric debugging which means when any + /// thread in a process stops, all other threads are stopped. The Suspend() + /// call here tells our process to suspend a thread and not let it run when + /// the other threads in a process are allowed to run. So when + /// SBProcess::Continue() is called, any threads that aren't suspended will + /// be allowed to run. If any of the SBThread functions for stepping are + /// called (StepOver, StepInto, StepOut, StepInstruction, RunToAddres), the + /// thread will not be allowed to run and these funtions will simply return. + /// + /// Eventually we plan to add support for thread centric debugging where + /// each thread is controlled individually and each thread would broadcast + /// its state, but we haven't implemented this yet. + /// + /// Likewise the SBThread::Resume() call will again allow the thread to run + /// when the process is continued. + /// + /// Suspend() and Resume() functions are not currently reference counted, if + /// anyone has the need for them to be reference counted, please let us + /// know. + //-------------------------------------------------------------------------- + bool + Suspend(); + + bool + Resume (); + + bool + IsSuspended(); + + bool + IsStopped(); + + uint32_t + GetNumFrames (); + + lldb::SBFrame + GetFrameAtIndex (uint32_t idx); + + lldb::SBFrame + GetSelectedFrame (); + + lldb::SBFrame + SetSelectedFrame (uint32_t frame_idx); + + static bool + EventIsThreadEvent (const SBEvent &event); + + static SBFrame + GetStackFrameFromEvent (const SBEvent &event); + + static SBThread + GetThreadFromEvent (const SBEvent &event); + + lldb::SBProcess + GetProcess (); + + const lldb::SBThread & + operator = (const lldb::SBThread &rhs); + + bool + operator == (const lldb::SBThread &rhs) const; + + bool + operator != (const lldb::SBThread &rhs) const; + + bool + GetDescription (lldb::SBStream &description) const; + + bool + GetStatus (lldb::SBStream &status) const; + +protected: + friend class SBBreakpoint; + friend class SBBreakpointLocation; + friend class SBFrame; + friend class SBProcess; + friend class SBDebugger; + friend class SBValue; + + void + SetThread (const lldb::ThreadSP& lldb_object_sp); + +#ifndef SWIG + SBError + ResumeNewPlan (lldb_private::ExecutionContext &exe_ctx, lldb_private::ThreadPlan *new_plan); +#endif + +private: + lldb::ExecutionContextRefSP m_opaque_sp; +}; + +} // namespace lldb + +#endif // LLDB_SBThread_h_ diff --git a/include/lldb/API/SBType.h b/include/lldb/API/SBType.h new file mode 100644 index 00000000000..3729b2f84b9 --- /dev/null +++ b/include/lldb/API/SBType.h @@ -0,0 +1,244 @@ +//===-- SBType.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBType_h_ +#define LLDB_SBType_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBTypeList; + +class SBTypeMember +{ +public: + SBTypeMember (); + + SBTypeMember (const lldb::SBTypeMember& rhs); + + ~SBTypeMember(); + + lldb::SBTypeMember& + operator = (const lldb::SBTypeMember& rhs); + + bool + IsValid() const; + + const char * + GetName (); + + lldb::SBType + GetType (); + + uint64_t + GetOffsetInBytes(); + + uint64_t + GetOffsetInBits(); + + bool + IsBitfield(); + + uint32_t + GetBitfieldSizeInBits(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + +protected: + friend class SBType; + + void + reset (lldb_private::TypeMemberImpl *); + + lldb_private::TypeMemberImpl & + ref (); + + const lldb_private::TypeMemberImpl & + ref () const; + + std::unique_ptr m_opaque_ap; +}; + +class SBType +{ +public: + + SBType(); + + SBType (const lldb::SBType &rhs); + + ~SBType (); + + bool + IsValid() const; + + uint64_t + GetByteSize(); + + bool + IsPointerType(); + + bool + IsReferenceType(); + + bool + IsFunctionType (); + + bool + IsPolymorphicClass (); + + lldb::SBType + GetPointerType(); + + lldb::SBType + GetPointeeType(); + + lldb::SBType + GetReferenceType(); + + lldb::SBType + GetDereferencedType(); + + lldb::SBType + GetUnqualifiedType(); + + lldb::SBType + GetCanonicalType(); + // Get the "lldb::BasicType" enumeration for a type. If a type is not a basic + // type eBasicTypeInvalid will be returned + lldb::BasicType + GetBasicType(); + + // The call below confusing and should really be renamed to "CreateBasicType" + lldb::SBType + GetBasicType(lldb::BasicType type); + + uint32_t + GetNumberOfFields (); + + uint32_t + GetNumberOfDirectBaseClasses (); + + uint32_t + GetNumberOfVirtualBaseClasses (); + + lldb::SBTypeMember + GetFieldAtIndex (uint32_t idx); + + lldb::SBTypeMember + GetDirectBaseClassAtIndex (uint32_t idx); + + lldb::SBTypeMember + GetVirtualBaseClassAtIndex (uint32_t idx); + + uint32_t + GetNumberOfTemplateArguments (); + + lldb::SBType + GetTemplateArgumentType (uint32_t idx); + + lldb::TemplateArgumentKind + GetTemplateArgumentKind (uint32_t idx); + + lldb::SBType + GetFunctionReturnType (); + + lldb::SBTypeList + GetFunctionArgumentTypes (); + + const char* + GetName(); + + lldb::TypeClass + GetTypeClass (); + + bool + IsTypeComplete (); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBType & + operator = (const lldb::SBType &rhs); + + bool + operator == (lldb::SBType &rhs); + + bool + operator != (lldb::SBType &rhs); + +protected: + + lldb_private::TypeImpl & + ref (); + + const lldb_private::TypeImpl & + ref () const; + + lldb::TypeImplSP + GetSP (); + + void + SetSP (const lldb::TypeImplSP &type_impl_sp); + + lldb::TypeImplSP m_opaque_sp; + + friend class SBFunction; + friend class SBModule; + friend class SBTarget; + friend class SBTypeNameSpecifier; + friend class SBTypeMember; + friend class SBTypeList; + friend class SBValue; + + SBType (const lldb_private::ClangASTType &); + SBType (const lldb::TypeSP &); + SBType (const lldb::TypeImplSP &); + +}; + +class SBTypeList +{ +public: + SBTypeList(); + + SBTypeList(const lldb::SBTypeList& rhs); + + ~SBTypeList(); + + lldb::SBTypeList& + operator = (const lldb::SBTypeList& rhs); + + bool + IsValid(); + + void + Append (lldb::SBType type); + + lldb::SBType + GetTypeAtIndex (uint32_t index); + + uint32_t + GetSize(); + + +private: + std::unique_ptr m_opaque_ap; + friend class SBModule; + friend class SBCompileUnit; +}; + + +} // namespace lldb + +#endif // LLDB_SBType_h_ diff --git a/include/lldb/API/SBTypeCategory.h b/include/lldb/API/SBTypeCategory.h new file mode 100644 index 00000000000..f123e931e17 --- /dev/null +++ b/include/lldb/API/SBTypeCategory.h @@ -0,0 +1,168 @@ +//===-- SBTypeCategory.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeCategory_h_ +#define LLDB_SBTypeCategory_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + + class SBTypeCategory + { + public: + + SBTypeCategory(); + + SBTypeCategory (const lldb::SBTypeCategory &rhs); + + ~SBTypeCategory (); + + bool + IsValid() const; + + bool + GetEnabled (); + + void + SetEnabled (bool); + + const char* + GetName(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + uint32_t + GetNumFormats (); + + uint32_t + GetNumSummaries (); + + uint32_t + GetNumFilters (); + +#ifndef LLDB_DISABLE_PYTHON + uint32_t + GetNumSynthetics (); +#endif + + SBTypeNameSpecifier + GetTypeNameSpecifierForFilterAtIndex (uint32_t); + + SBTypeNameSpecifier + GetTypeNameSpecifierForFormatAtIndex (uint32_t); + + SBTypeNameSpecifier + GetTypeNameSpecifierForSummaryAtIndex (uint32_t); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeNameSpecifier + GetTypeNameSpecifierForSyntheticAtIndex (uint32_t); +#endif + + SBTypeFilter + GetFilterForType (SBTypeNameSpecifier); + + SBTypeFormat + GetFormatForType (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSummary + GetSummaryForType (SBTypeNameSpecifier); +#endif + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSynthetic + GetSyntheticForType (SBTypeNameSpecifier); +#endif + +#ifndef LLDB_DISABLE_PYTHON + SBTypeFilter + GetFilterAtIndex (uint32_t); +#endif + + SBTypeFormat + GetFormatAtIndex (uint32_t); + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSummary + GetSummaryAtIndex (uint32_t); +#endif + +#ifndef LLDB_DISABLE_PYTHON + SBTypeSynthetic + GetSyntheticAtIndex (uint32_t); +#endif + + bool + AddTypeFormat (SBTypeNameSpecifier, + SBTypeFormat); + + bool + DeleteTypeFormat (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + bool + AddTypeSummary (SBTypeNameSpecifier, + SBTypeSummary); +#endif + + bool + DeleteTypeSummary (SBTypeNameSpecifier); + + bool + AddTypeFilter (SBTypeNameSpecifier, + SBTypeFilter); + + bool + DeleteTypeFilter (SBTypeNameSpecifier); + +#ifndef LLDB_DISABLE_PYTHON + bool + AddTypeSynthetic (SBTypeNameSpecifier, + SBTypeSynthetic); + + bool + DeleteTypeSynthetic (SBTypeNameSpecifier); +#endif + + lldb::SBTypeCategory & + operator = (const lldb::SBTypeCategory &rhs); + + bool + operator == (lldb::SBTypeCategory &rhs); + + bool + operator != (lldb::SBTypeCategory &rhs); + + protected: + friend class SBDebugger; + + lldb::TypeCategoryImplSP + GetSP (); + + void + SetSP (const lldb::TypeCategoryImplSP &typecategory_impl_sp); + + TypeCategoryImplSP m_opaque_sp; + + SBTypeCategory (const lldb::TypeCategoryImplSP &); + + SBTypeCategory (const char*); + + bool + IsDefaultCategory(); + + }; + +} // namespace lldb + +#endif // LLDB_SBTypeCategory_h_ diff --git a/include/lldb/API/SBTypeFilter.h b/include/lldb/API/SBTypeFilter.h new file mode 100644 index 00000000000..016954943e2 --- /dev/null +++ b/include/lldb/API/SBTypeFilter.h @@ -0,0 +1,92 @@ +//===-- SBTypeFilter.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeFilter_h_ +#define LLDB_SBTypeFilter_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + + class SBTypeFilter + { + public: + + SBTypeFilter(); + + SBTypeFilter (uint32_t options); // see lldb::eTypeOption values + + SBTypeFilter (const lldb::SBTypeFilter &rhs); + + ~SBTypeFilter (); + + bool + IsValid() const; + + uint32_t + GetNumberOfExpressionPaths (); + + const char* + GetExpressionPathAtIndex (uint32_t i); + + bool + ReplaceExpressionPathAtIndex (uint32_t i, const char* item); + + void + AppendExpressionPath (const char* item); + + void + Clear(); + + uint32_t + GetOptions(); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeFilter & + operator = (const lldb::SBTypeFilter &rhs); + + bool + IsEqualTo (lldb::SBTypeFilter &rhs); + + bool + operator == (lldb::SBTypeFilter &rhs); + + bool + operator != (lldb::SBTypeFilter &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::TypeFilterImplSP + GetSP (); + + void + SetSP (const lldb::TypeFilterImplSP &typefilter_impl_sp); + + lldb::TypeFilterImplSP m_opaque_sp; + + SBTypeFilter (const lldb::TypeFilterImplSP &); + + bool + CopyOnWrite_Impl(); + + }; + + +} // namespace lldb + +#endif // LLDB_SBTypeFilter_h_ diff --git a/include/lldb/API/SBTypeFormat.h b/include/lldb/API/SBTypeFormat.h new file mode 100644 index 00000000000..cd6345fbe6f --- /dev/null +++ b/include/lldb/API/SBTypeFormat.h @@ -0,0 +1,84 @@ +//===-- SBTypeFormat.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeFormat_h_ +#define LLDB_SBTypeFormat_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBTypeFormat +{ +public: + + SBTypeFormat(); + + SBTypeFormat (lldb::Format format, + uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeFormat (const lldb::SBTypeFormat &rhs); + + ~SBTypeFormat (); + + bool + IsValid() const; + + lldb::Format + GetFormat (); + + uint32_t + GetOptions(); + + void + SetFormat (lldb::Format); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeFormat & + operator = (const lldb::SBTypeFormat &rhs); + + bool + IsEqualTo (lldb::SBTypeFormat &rhs); + + bool + operator == (lldb::SBTypeFormat &rhs); + + bool + operator != (lldb::SBTypeFormat &rhs); + +protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::TypeFormatImplSP + GetSP (); + + void + SetSP (const lldb::TypeFormatImplSP &typeformat_impl_sp); + + lldb::TypeFormatImplSP m_opaque_sp; + + SBTypeFormat (const lldb::TypeFormatImplSP &); + + bool + CopyOnWrite_Impl(); + +}; + + +} // namespace lldb + +#endif // LLDB_SBTypeFormat_h_ diff --git a/include/lldb/API/SBTypeNameSpecifier.h b/include/lldb/API/SBTypeNameSpecifier.h new file mode 100644 index 00000000000..19d1988aa0c --- /dev/null +++ b/include/lldb/API/SBTypeNameSpecifier.h @@ -0,0 +1,77 @@ +//===-- SBTypeNameSpecifier.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeNameSpecifier_h_ +#define LLDB_SBTypeNameSpecifier_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + + class SBTypeNameSpecifier + { + public: + + SBTypeNameSpecifier(); + + SBTypeNameSpecifier (const char* name, + bool is_regex = false); + + SBTypeNameSpecifier (SBType type); + + SBTypeNameSpecifier (const lldb::SBTypeNameSpecifier &rhs); + + ~SBTypeNameSpecifier (); + + bool + IsValid() const; + + const char* + GetName(); + + SBType + GetType (); + + bool + IsRegex(); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeNameSpecifier & + operator = (const lldb::SBTypeNameSpecifier &rhs); + + bool + IsEqualTo (lldb::SBTypeNameSpecifier &rhs); + + bool + operator == (lldb::SBTypeNameSpecifier &rhs); + + bool + operator != (lldb::SBTypeNameSpecifier &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + + lldb::TypeNameSpecifierImplSP + GetSP (); + + void + SetSP (const lldb::TypeNameSpecifierImplSP &type_namespec_sp); + + lldb::TypeNameSpecifierImplSP m_opaque_sp; + + SBTypeNameSpecifier (const lldb::TypeNameSpecifierImplSP &); + }; + +} // namespace lldb + +#endif // LLDB_SBTypeNameSpecifier_h_ diff --git a/include/lldb/API/SBTypeSummary.h b/include/lldb/API/SBTypeSummary.h new file mode 100644 index 00000000000..67a8607511c --- /dev/null +++ b/include/lldb/API/SBTypeSummary.h @@ -0,0 +1,115 @@ +//===-- SBTypeSummary.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeSummary_h_ +#define LLDB_SBTypeSummary_h_ + +#include "lldb/API/SBDefines.h" + +#ifndef LLDB_DISABLE_PYTHON + +namespace lldb { + + class SBTypeSummary + { + public: + + SBTypeSummary(); + + static SBTypeSummary + CreateWithSummaryString (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + static SBTypeSummary + CreateWithFunctionName (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + static SBTypeSummary + CreateWithScriptCode (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeSummary (const lldb::SBTypeSummary &rhs); + + ~SBTypeSummary (); + + bool + IsValid() const; + + bool + IsFunctionCode(); + + bool + IsFunctionName(); + + bool + IsSummaryString(); + + const char* + GetData (); + + void + SetSummaryString (const char* data); + + void + SetFunctionName (const char* data); + + void + SetFunctionCode (const char* data); + + uint32_t + GetOptions (); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeSummary & + operator = (const lldb::SBTypeSummary &rhs); + + bool + IsEqualTo (lldb::SBTypeSummary &rhs); + + bool + operator == (lldb::SBTypeSummary &rhs); + + bool + operator != (lldb::SBTypeSummary &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::TypeSummaryImplSP + GetSP (); + + void + SetSP (const lldb::TypeSummaryImplSP &typefilter_impl_sp); + + lldb::TypeSummaryImplSP m_opaque_sp; + + SBTypeSummary (const lldb::TypeSummaryImplSP &); + + bool + CopyOnWrite_Impl(); + + bool + ChangeSummaryType (bool want_script); + + }; + + +} // namespace lldb + +#endif // LLDB_DISABLE_PYTHON + +#endif // LLDB_SBTypeSummary_h_ diff --git a/include/lldb/API/SBTypeSynthetic.h b/include/lldb/API/SBTypeSynthetic.h new file mode 100644 index 00000000000..e77cbfef598 --- /dev/null +++ b/include/lldb/API/SBTypeSynthetic.h @@ -0,0 +1,102 @@ +//===-- SBTypeSynthetic.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBTypeSynthetic_h_ +#define LLDB_SBTypeSynthetic_h_ + +#include "lldb/API/SBDefines.h" + +#ifndef LLDB_DISABLE_PYTHON + +namespace lldb { + + class SBTypeSynthetic + { + public: + + SBTypeSynthetic(); + + static SBTypeSynthetic + CreateWithClassName (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + static SBTypeSynthetic + CreateWithScriptCode (const char* data, + uint32_t options = 0); // see lldb::eTypeOption values + + SBTypeSynthetic (const lldb::SBTypeSynthetic &rhs); + + ~SBTypeSynthetic (); + + bool + IsValid() const; + + bool + IsClassCode(); + + bool + IsClassName(); + + const char* + GetData (); + + void + SetClassName (const char* data); + + void + SetClassCode (const char* data); + + uint32_t + GetOptions (); + + void + SetOptions (uint32_t); + + bool + GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level); + + lldb::SBTypeSynthetic & + operator = (const lldb::SBTypeSynthetic &rhs); + + bool + IsEqualTo (lldb::SBTypeSynthetic &rhs); + + bool + operator == (lldb::SBTypeSynthetic &rhs); + + bool + operator != (lldb::SBTypeSynthetic &rhs); + + protected: + friend class SBDebugger; + friend class SBTypeCategory; + friend class SBValue; + + lldb::ScriptedSyntheticChildrenSP + GetSP (); + + void + SetSP (const lldb::ScriptedSyntheticChildrenSP &typefilter_impl_sp); + + lldb::ScriptedSyntheticChildrenSP m_opaque_sp; + + SBTypeSynthetic (const lldb::ScriptedSyntheticChildrenSP &); + + bool + CopyOnWrite_Impl(); + + }; + + +} // namespace lldb + +#endif // LLDB_DISABLE_PYTHON + +#endif // LLDB_SBTypeSynthetic_h_ diff --git a/include/lldb/API/SBValue.h b/include/lldb/API/SBValue.h new file mode 100644 index 00000000000..2b9a344b930 --- /dev/null +++ b/include/lldb/API/SBValue.h @@ -0,0 +1,488 @@ +//===-- SBValue.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBValue_h_ +#define LLDB_SBValue_h_ + +#include "lldb/API/SBData.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBType.h" + +class ValueImpl; +class ValueLocker; + +namespace lldb { + +class SBValue +{ +friend class ValueLocker; + +public: + SBValue (); + + SBValue (const lldb::SBValue &rhs); + + lldb::SBValue & + operator =(const lldb::SBValue &rhs); + + ~SBValue (); + + bool + IsValid(); + + void + Clear(); + + SBError + GetError(); + + lldb::user_id_t + GetID (); + + const char * + GetName(); + + const char * + GetTypeName (); + + size_t + GetByteSize (); + + bool + IsInScope (); + + lldb::Format + GetFormat (); + + void + SetFormat (lldb::Format format); + + const char * + GetValue (); + + int64_t + GetValueAsSigned (lldb::SBError& error, int64_t fail_value=0); + + uint64_t + GetValueAsUnsigned (lldb::SBError& error, uint64_t fail_value=0); + + int64_t + GetValueAsSigned(int64_t fail_value=0); + + uint64_t + GetValueAsUnsigned(uint64_t fail_value=0); + + ValueType + GetValueType (); + + bool + GetValueDidChange (); + + const char * + GetSummary (); + + const char * + GetObjectDescription (); + + lldb::SBValue + GetDynamicValue (lldb::DynamicValueType use_dynamic); + + lldb::SBValue + GetStaticValue (); + + lldb::SBValue + GetNonSyntheticValue (); + + lldb::DynamicValueType + GetPreferDynamicValue (); + + void + SetPreferDynamicValue (lldb::DynamicValueType use_dynamic); + + bool + GetPreferSyntheticValue (); + + void + SetPreferSyntheticValue (bool use_synthetic); + + bool + IsDynamic (); + + bool + IsSynthetic (); + + const char * + GetLocation (); + + // Deprecated - use the one that takes SBError& + bool + SetValueFromCString (const char *value_str); + + bool + SetValueFromCString (const char *value_str, lldb::SBError& error); + + lldb::SBTypeFormat + GetTypeFormat (); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SBTypeSummary + GetTypeSummary (); +#endif + + lldb::SBTypeFilter + GetTypeFilter (); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SBTypeSynthetic + GetTypeSynthetic (); +#endif + + lldb::SBValue + GetChildAtIndex (uint32_t idx); + + lldb::SBValue + CreateChildAtOffset (const char *name, uint32_t offset, lldb::SBType type); + + lldb::SBValue + Cast (lldb::SBType type); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expression); + + lldb::SBValue + CreateValueFromExpression (const char *name, const char* expression, SBExpressionOptions &options); + + lldb::SBValue + CreateValueFromAddress (const char* name, + lldb::addr_t address, + lldb::SBType type); + + // this has no address! GetAddress() and GetLoadAddress() as well as AddressOf() + // on the return of this call all return invalid + lldb::SBValue + CreateValueFromData (const char* name, + lldb::SBData data, + lldb::SBType type); + + //------------------------------------------------------------------ + /// Get a child value by index from a value. + /// + /// Structs, unions, classes, arrays and and pointers have child + /// values that can be access by index. + /// + /// Structs and unions access child members using a zero based index + /// for each child member. For + /// + /// Classes reserve the first indexes for base classes that have + /// members (empty base classes are omitted), and all members of the + /// current class will then follow the base classes. + /// + /// Pointers differ depending on what they point to. If the pointer + /// points to a simple type, the child at index zero + /// is the only child value available, unless \a synthetic_allowed + /// is \b true, in which case the pointer will be used as an array + /// and can create 'synthetic' child values using positive or + /// negative indexes. If the pointer points to an aggregate type + /// (an array, class, union, struct), then the pointee is + /// transparently skipped and any children are going to be the indexes + /// of the child values within the aggregate type. For example if + /// we have a 'Point' type and we have a SBValue that contains a + /// pointer to a 'Point' type, then the child at index zero will be + /// the 'x' member, and the child at index 1 will be the 'y' member + /// (the child at index zero won't be a 'Point' instance). + /// + /// Arrays have a preset number of children that can be accessed by + /// index and will returns invalid child values for indexes that are + /// out of bounds unless the \a synthetic_allowed is \b true. In this + /// case the array can create 'synthetic' child values for indexes + /// that aren't in the array bounds using positive or negative + /// indexes. + /// + /// @param[in] idx + /// The index of the child value to get + /// + /// @param[in] use_dynamic + /// An enumeration that specifies wether to get dynamic values, + /// and also if the target can be run to figure out the dynamic + /// type of the child value. + /// + /// @param[in] synthetic_allowed + /// If \b true, then allow child values to be created by index + /// for pointers and arrays for indexes that normally wouldn't + /// be allowed. + /// + /// @return + /// A new SBValue object that represents the child member value. + //------------------------------------------------------------------ + lldb::SBValue + GetChildAtIndex (uint32_t idx, + lldb::DynamicValueType use_dynamic, + bool can_create_synthetic); + + // Matches children of this object only and will match base classes and + // member names if this is a clang typed object. + uint32_t + GetIndexOfChildWithName (const char *name); + + // Matches child members of this object and child members of any base + // classes. + lldb::SBValue + GetChildMemberWithName (const char *name); + + // Matches child members of this object and child members of any base + // classes. + lldb::SBValue + GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dynamic); + + // Expands nested expressions like .a->b[0].c[1]->d + lldb::SBValue + GetValueForExpressionPath(const char* expr_path); + + lldb::SBValue + AddressOf(); + + lldb::addr_t + GetLoadAddress(); + + lldb::SBAddress + GetAddress(); + + //------------------------------------------------------------------ + /// Get an SBData wrapping what this SBValue points to. + /// + /// This method will dereference the current SBValue, if its + /// data type is a T* or T[], and extract item_count elements + /// of type T from it, copying their contents in an SBData. + /// + /// @param[in] item_idx + /// The index of the first item to retrieve. For an array + /// this is equivalent to array[item_idx], for a pointer + /// to *(pointer + item_idx). In either case, the measurement + /// unit for item_idx is the sizeof(T) rather than the byte + /// + /// @param[in] item_count + /// How many items should be copied into the output. By default + /// only one item is copied, but more can be asked for. + /// + /// @return + /// An SBData with the contents of the copied items, on success. + /// An empty SBData otherwise. + //------------------------------------------------------------------ + lldb::SBData + GetPointeeData (uint32_t item_idx = 0, + uint32_t item_count = 1); + + //------------------------------------------------------------------ + /// Get an SBData wrapping the contents of this SBValue. + /// + /// This method will read the contents of this object in memory + /// and copy them into an SBData for future use. + /// + /// @return + /// An SBData with the contents of this SBValue, on success. + /// An empty SBData otherwise. + //------------------------------------------------------------------ + lldb::SBData + GetData (); + + bool + SetData (lldb::SBData &data, lldb::SBError& error); + + lldb::SBDeclaration + GetDeclaration (); + + //------------------------------------------------------------------ + /// Find out if a SBValue might have children. + /// + /// This call is much more efficient than GetNumChildren() as it + /// doesn't need to complete the underlying type. This is designed + /// to be used in a UI environment in order to detect if the + /// disclosure triangle should be displayed or not. + /// + /// This function returns true for class, union, structure, + /// pointers, references, arrays and more. Again, it does so without + /// doing any expensive type completion. + /// + /// @return + /// Returns \b true if the SBValue might have children, or \b + /// false otherwise. + //------------------------------------------------------------------ + bool + MightHaveChildren (); + + uint32_t + GetNumChildren (); + + void * + GetOpaqueType(); + + lldb::SBTarget + GetTarget(); + + lldb::SBProcess + GetProcess(); + + lldb::SBThread + GetThread(); + + lldb::SBFrame + GetFrame(); + + lldb::SBValue + Dereference (); + + bool + TypeIsPointerType (); + + lldb::SBType + GetType(); + + bool + GetDescription (lldb::SBStream &description); + + bool + GetExpressionPath (lldb::SBStream &description); + + bool + GetExpressionPath (lldb::SBStream &description, + bool qualify_cxx_base_classes); + + SBValue (const lldb::ValueObjectSP &value_sp); + + //------------------------------------------------------------------ + /// Watch this value if it resides in memory. + /// + /// Sets a watchpoint on the value. + /// + /// @param[in] resolve_location + /// Resolve the location of this value once and watch its address. + /// This value must currently be set to \b true as watching all + /// locations of a variable or a variable path is not yet supported, + /// though we plan to support it in the future. + /// + /// @param[in] read + /// Stop when this value is accessed. + /// + /// @param[in] write + /// Stop when this value is modified + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// An SBWatchpoint object. This object might not be valid upon + /// return due to a value not being contained in memory, too + /// large, or watchpoint resources are not available or all in + /// use. + //------------------------------------------------------------------ + lldb::SBWatchpoint + Watch (bool resolve_location, bool read, bool write, SBError &error); + + // Backward compatibility fix in the interim. + lldb::SBWatchpoint + Watch (bool resolve_location, bool read, bool write); + + //------------------------------------------------------------------ + /// Watch this value that this value points to in memory + /// + /// Sets a watchpoint on the value. + /// + /// @param[in] resolve_location + /// Resolve the location of this value once and watch its address. + /// This value must currently be set to \b true as watching all + /// locations of a variable or a variable path is not yet supported, + /// though we plan to support it in the future. + /// + /// @param[in] read + /// Stop when this value is accessed. + /// + /// @param[in] write + /// Stop when this value is modified + /// + /// @param[out] + /// An error object. Contains the reason if there is some failure. + /// + /// @return + /// An SBWatchpoint object. This object might not be valid upon + /// return due to a value not being contained in memory, too + /// large, or watchpoint resources are not available or all in + /// use. + //------------------------------------------------------------------ + lldb::SBWatchpoint + WatchPointee (bool resolve_location, bool read, bool write, SBError &error); + + //------------------------------------------------------------------ + /// Same as the protected version of GetSP that takes a locker, except that we make the + /// locker locally in the function. Since the Target API mutex is recursive, and the + /// StopLocker is a read lock, you can call this function even if you are already + /// holding the two above-mentioned locks. + /// + /// @return + /// A ValueObjectSP of the best kind (static, dynamic or synthetic) we + /// can cons up, in accordance with the SBValue's settings. + //------------------------------------------------------------------ + lldb::ValueObjectSP + GetSP () const; + +protected: + friend class SBBlock; + friend class SBFrame; + friend class SBTarget; + friend class SBThread; + friend class SBValueList; + + //------------------------------------------------------------------ + /// Get the appropriate ValueObjectSP from this SBValue, consulting the + /// use_dynamic and use_synthetic options passed in to SetSP when the + /// SBValue's contents were set. Since this often requires examining memory, + /// and maybe even running code, it needs to acquire the Target API and Process StopLock. + /// Those are held in an opaque class ValueLocker which is currently local to SBValue.cpp. + /// So you don't have to get these yourself just default construct a ValueLocker, and pass it into this. + /// If we need to make a ValueLocker and use it in some other .cpp file, we'll have to move it to + /// ValueObject.h/cpp or somewhere else convenient. We haven't needed to so far. + /// + /// @param[in] value_locker + /// An object that will hold the Target API, and Process RunLocks, and + /// auto-destroy them when it goes out of scope. Currently this is only useful in + /// SBValue.cpp. + /// + /// @return + /// A ValueObjectSP of the best kind (static, dynamic or synthetic) we + /// can cons up, in accordance with the SBValue's settings. + //------------------------------------------------------------------ + lldb::ValueObjectSP + GetSP (ValueLocker &value_locker) const; + + // these calls do the right thing WRT adjusting their settings according to the target's preferences + void + SetSP (const lldb::ValueObjectSP &sp); + + void + SetSP (const lldb::ValueObjectSP &sp, bool use_synthetic); + + void + SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic); + + void + SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic, bool use_synthetic); + + void + SetSP (const lldb::ValueObjectSP &sp, lldb::DynamicValueType use_dynamic, bool use_synthetic, const char *name); + +private: + typedef std::shared_ptr ValueImplSP; + ValueImplSP m_opaque_sp; + + void + SetSP (ValueImplSP impl_sp); +}; + +} // namespace lldb + +#endif // LLDB_SBValue_h_ diff --git a/include/lldb/API/SBValueList.h b/include/lldb/API/SBValueList.h new file mode 100644 index 00000000000..b9a6aedea3c --- /dev/null +++ b/include/lldb/API/SBValueList.h @@ -0,0 +1,93 @@ +//===-- SBValueList.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBValueList_h_ +#define LLDB_SBValueList_h_ + +#include "lldb/API/SBDefines.h" + +class ValueListImpl; + +namespace lldb { + +class SBValueList +{ +public: + + SBValueList (); + + SBValueList (const lldb::SBValueList &rhs); + + ~SBValueList(); + + bool + IsValid() const; + + void + Clear(); + + void + Append (const lldb::SBValue &val_obj); + + void + Append (const lldb::SBValueList& value_list); + + uint32_t + GetSize() const; + + lldb::SBValue + GetValueAtIndex (uint32_t idx) const; + + lldb::SBValue + FindValueObjectByUID (lldb::user_id_t uid); + + const lldb::SBValueList & + operator = (const lldb::SBValueList &rhs); + +protected: + + // only useful for visualizing the pointer or comparing two SBValueLists + // to see if they are backed by the same underlying Impl. + void * + opaque_ptr (); + +private: + friend class SBFrame; + + SBValueList (const ValueListImpl *lldb_object_ptr); + + void + Append (lldb::ValueObjectSP& val_obj_sp); + + void + CreateIfNeeded (); + + ValueListImpl * + operator -> (); + + ValueListImpl & + operator* (); + + const ValueListImpl * + operator -> () const; + + const ValueListImpl & + operator* () const; + + + ValueListImpl & + ref (); + + std::unique_ptr m_opaque_ap; +}; + + +} // namespace lldb + +#endif // LLDB_SBValueList_h_ diff --git a/include/lldb/API/SBWatchpoint.h b/include/lldb/API/SBWatchpoint.h new file mode 100644 index 00000000000..9bf51fd1ad0 --- /dev/null +++ b/include/lldb/API/SBWatchpoint.h @@ -0,0 +1,104 @@ +//===-- SBWatchpoint.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SBWatchpoint_h_ +#define LLDB_SBWatchpoint_h_ + +#include "lldb/API/SBDefines.h" + +namespace lldb { + +class SBWatchpoint +{ +public: + + SBWatchpoint (); + + SBWatchpoint (const lldb::SBWatchpoint &rhs); + + SBWatchpoint (const lldb::WatchpointSP &wp_sp); + + ~SBWatchpoint (); + + const lldb::SBWatchpoint & + operator = (const lldb::SBWatchpoint &rhs); + + bool + IsValid() const; + + SBError + GetError(); + + watch_id_t + GetID (); + + /// With -1 representing an invalid hardware index. + int32_t + GetHardwareIndex (); + + lldb::addr_t + GetWatchAddress (); + + size_t + GetWatchSize(); + + void + SetEnabled(bool enabled); + + bool + IsEnabled (); + + uint32_t + GetHitCount (); + + uint32_t + GetIgnoreCount (); + + void + SetIgnoreCount (uint32_t n); + + const char * + GetCondition (); + + void + SetCondition (const char *condition); + + bool + GetDescription (lldb::SBStream &description, DescriptionLevel level); + + void + Clear (); + + lldb::WatchpointSP + GetSP () const; + + void + SetSP (const lldb::WatchpointSP &sp); + + static bool + EventIsWatchpointEvent (const lldb::SBEvent &event); + + static lldb::WatchpointEventType + GetWatchpointEventTypeFromEvent (const lldb::SBEvent& event); + + static lldb::SBWatchpoint + GetWatchpointFromEvent (const lldb::SBEvent& event); + +private: + friend class SBTarget; + friend class SBValue; + + + lldb::WatchpointSP m_opaque_sp; + +}; + +} // namespace lldb + +#endif // LLDB_SBWatchpoint_h_ diff --git a/include/lldb/Breakpoint/Breakpoint.h b/include/lldb/Breakpoint/Breakpoint.h new file mode 100644 index 00000000000..bd11a1c91e2 --- /dev/null +++ b/include/lldb/Breakpoint/Breakpoint.h @@ -0,0 +1,630 @@ +//===-- Breakpoint.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Breakpoint_h_ +#define liblldb_Breakpoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationList.h" +#include "lldb/Breakpoint/BreakpointOptions.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/Stoppoint.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Breakpoint Breakpoint.h "lldb/Breakpoint/Breakpoint.h" +/// @brief Class that manages logical breakpoint setting. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// A breakpoint has four main parts, a filter, a resolver, the list of breakpoint +/// locations that have been determined for the filter/resolver pair, and finally +/// a set of options for the breakpoint. +/// +/// \b Filter: +/// This is an object derived from SearchFilter. It manages the search +/// for breakpoint location matches through the symbols in the module list of the target +/// that owns it. It also filters out locations based on whatever logic it wants. +/// +/// \b Resolver: +/// This is an object derived from BreakpointResolver. It provides a +/// callback to the filter that will find breakpoint locations. How it does this is +/// determined by what kind of resolver it is. +/// +/// The Breakpoint class also provides constructors for the common breakpoint cases +/// which make the appropriate filter and resolver for you. +/// +/// \b Location List: +/// This stores the breakpoint locations that have been determined +/// to date. For a given breakpoint, there will be only one location with a given +/// address. Adding a location at an already taken address will just return the location +/// already at that address. Locations can be looked up by ID, or by address. +/// +/// \b Options: +/// This includes: +/// \b Enabled/Disabled +/// \b Ignore Count +/// \b Callback +/// \b Condition +/// Note, these options can be set on the breakpoint, and they can also be set on the +/// individual locations. The options set on the breakpoint take precedence over the +/// options set on the individual location. +/// So for instance disabling the breakpoint will cause NONE of the locations to get hit. +/// But if the breakpoint is enabled, then the location's enabled state will be checked +/// to determine whether to insert that breakpoint location. +/// Similarly, if the breakpoint condition says "stop", we won't check the location's condition. +/// But if the breakpoint condition says "continue", then we will check the location for whether +/// to actually stop or not. +/// One subtle point worth observing here is that you don't actually stop at a Breakpoint, you +/// always stop at one of its locations. So the "should stop" tests are done by the location, +/// not by the breakpoint. +//---------------------------------------------------------------------- +class Breakpoint: + public std::enable_shared_from_this, + public Stoppoint +{ +public: + + static const ConstString & + GetEventIdentifier (); + + + //------------------------------------------------------------------ + /// An enum specifying the match style for breakpoint settings. At + /// present only used for function name style breakpoints. + //------------------------------------------------------------------ + typedef enum + { + Exact, + Regexp, + Glob + } MatchType; + + class BreakpointEventData : + public EventData + { + public: + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + BreakpointEventData (lldb::BreakpointEventType sub_type, + const lldb::BreakpointSP &new_breakpoint_sp); + + virtual + ~BreakpointEventData(); + + lldb::BreakpointEventType + GetBreakpointEventType () const; + + lldb::BreakpointSP & + GetBreakpoint (); + + BreakpointLocationCollection & + GetBreakpointLocationCollection() + { + return m_locations; + } + + + virtual void + Dump (Stream *s) const; + + static lldb::BreakpointEventType + GetBreakpointEventTypeFromEvent (const lldb::EventSP &event_sp); + + static lldb::BreakpointSP + GetBreakpointFromEvent (const lldb::EventSP &event_sp); + + static lldb::BreakpointLocationSP + GetBreakpointLocationAtIndexFromEvent (const lldb::EventSP &event_sp, uint32_t loc_idx); + + static size_t + GetNumBreakpointLocationsFromEvent (const lldb::EventSP &event_sp); + + static const BreakpointEventData * + GetEventDataFromEvent (const Event *event_sp); + + private: + + lldb::BreakpointEventType m_breakpoint_event; + lldb::BreakpointSP m_new_breakpoint_sp; + BreakpointLocationCollection m_locations; + + DISALLOW_COPY_AND_ASSIGN (BreakpointEventData); + }; + + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is not virtual since there should be no reason to subclass + /// breakpoints. The varieties of breakpoints are specified instead by + /// providing different resolvers & filters. + //------------------------------------------------------------------ + ~Breakpoint(); + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Tell whether this breakpoint is an "internal" breakpoint. + /// @return + /// Returns \b true if this is an internal breakpoint, \b false otherwise. + //------------------------------------------------------------------ + bool + IsInternal () const; + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s); + + //------------------------------------------------------------------ + // The next set of methods provide ways to tell the breakpoint to update + // it's location list - usually done when modules appear or disappear. + //------------------------------------------------------------------ + + + //------------------------------------------------------------------ + /// Tell this breakpoint to clear all its breakpoint sites. Done + /// when the process holding the breakpoint sites is destroyed. + //------------------------------------------------------------------ + void + ClearAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Tell this breakpoint to scan it's target's module list and resolve any + /// new locations that match the breakpoint's specifications. + //------------------------------------------------------------------ + void + ResolveBreakpoint (); + + //------------------------------------------------------------------ + /// Tell this breakpoint to scan a given module list and resolve any + /// new locations that match the breakpoint's specifications. + /// + /// @param[in] changed_modules + /// The list of modules to look in for new locations. + //------------------------------------------------------------------ + void + ResolveBreakpointInModules (ModuleList &changed_modules); + + + //------------------------------------------------------------------ + /// Like ResolveBreakpointInModules, but allows for "unload" events, in + /// which case we will remove any locations that are in modules that got + /// unloaded. + /// + /// @param[in] changedModules + /// The list of modules to look in for new locations. + /// @param[in] load_event + /// If \b true then the modules were loaded, if \b false, unloaded. + /// @param[in] delete_locations + /// If \b true then the modules were unloaded delete any locations in the changed modules. + //------------------------------------------------------------------ + void + ModulesChanged (ModuleList &changed_modules, + bool load_event, + bool delete_locations = false); + + + //------------------------------------------------------------------ + /// Tells the breakpoint the old module \a old_module_sp has been + /// replaced by new_module_sp (usually because the underlying file has been + /// rebuilt, and the old version is gone.) + /// + /// @param[in] old_module_sp + /// The old module that is going away. + /// @param[in] new_module_sp + /// The new module that is replacing it. + //------------------------------------------------------------------ + void + ModuleReplaced (lldb::ModuleSP old_module_sp, lldb::ModuleSP new_module_sp); + + //------------------------------------------------------------------ + // The next set of methods provide access to the breakpoint locations + // for this breakpoint. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Add a location to the breakpoint's location list. This is only meant + /// to be called by the breakpoint's resolver. FIXME: how do I ensure that? + /// + /// @param[in] addr + /// The Address specifying the new location. + /// @param[out] new_location + /// Set to \b true if a new location was created, to \b false if there + /// already was a location at this Address. + /// @return + /// Returns a pointer to the new location. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + AddLocation (const Address &addr, + bool *new_location = NULL); + + //------------------------------------------------------------------ + /// Find a breakpoint location by Address. + /// + /// @param[in] addr + /// The Address specifying the location. + /// @return + /// Returns a shared pointer to the location at \a addr. The pointer + /// in the shared pointer will be NULL if there is no location at that address. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindLocationByAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Find a breakpoint location ID by Address. + /// + /// @param[in] addr + /// The Address specifying the location. + /// @return + /// Returns the UID of the location at \a addr, or \b LLDB_INVALID_ID if + /// there is no breakpoint location at that address. + //------------------------------------------------------------------ + lldb::break_id_t + FindLocationIDByAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Find a breakpoint location for a given breakpoint location ID. + /// + /// @param[in] bp_loc_id + /// The ID specifying the location. + /// @return + /// Returns a shared pointer to the location with ID \a bp_loc_id. The pointer + /// in the shared pointer will be NULL if there is no location with that ID. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindLocationByID (lldb::break_id_t bp_loc_id); + + //------------------------------------------------------------------ + /// Get breakpoint locations by index. + /// + /// @param[in] index + /// The location index. + /// + /// @return + /// Returns a shared pointer to the location with index \a + /// index. The shared pointer might contain NULL if \a index is + /// greater than then number of actual locations. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetLocationAtIndex (size_t index); + + //------------------------------------------------------------------ + // The next section deals with various breakpoint options. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetEnabled (bool enable); + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled (); + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + void + SetIgnoreCount (uint32_t count); + + //------------------------------------------------------------------ + /// Return the current ignore count/ + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + uint32_t + GetIgnoreCount () const; + + //------------------------------------------------------------------ + /// Return the current hit count for all locations. + /// @return + /// The current hit count for all locations. + //------------------------------------------------------------------ + uint32_t + GetHitCount () const; + + + //------------------------------------------------------------------ + /// If \a one_shot is \b true, breakpoint will be deleted on first hit. + //------------------------------------------------------------------ + void + SetOneShot (bool one_shot); + + //------------------------------------------------------------------ + /// Check the OneShot state. + /// @return + /// \b true if the breakpoint is one shot, \b false otherwise. + //------------------------------------------------------------------ + bool + IsOneShot () const; + + //------------------------------------------------------------------ + /// Set the valid thread to be checked when the breakpoint is hit. + /// @param[in] thread_id + /// If this thread hits the breakpoint, we stop, otherwise not. + //------------------------------------------------------------------ + void + SetThreadID (lldb::tid_t thread_id); + + //------------------------------------------------------------------ + /// Return the current stop thread value. + /// @return + /// The thread id for which the breakpoint hit will stop, LLDB_INVALID_THREAD_ID for all threads. + //------------------------------------------------------------------ + lldb::tid_t + GetThreadID () const; + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + //------------------------------------------------------------------ + /// Set the callback action invoked when the breakpoint is hit. + /// + /// @param[in] callback + /// The method that will get called when the breakpoint is hit. + /// @param[in] baton + /// A void * pointer that will get passed back to the callback function. + /// @param[in] is_synchronous + /// If \b true the callback will be run on the private event thread + /// before the stop event gets reported. If false, the callback will get + /// handled on the public event thead after the stop has been posted. + /// + /// @return + /// \b true if the process should stop when you hit the breakpoint. + /// \b false if it should continue. + //------------------------------------------------------------------ + void + SetCallback (BreakpointHitCallback callback, + void *baton, + bool is_synchronous = false); + + void + SetCallback (BreakpointHitCallback callback, + const lldb::BatonSP &callback_baton_sp, + bool is_synchronous = false); + + void + ClearCallback (); + + //------------------------------------------------------------------ + /// Set the breakpoint's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the breakpoint is hit. + /// Pass in NULL to clear the condition. + //------------------------------------------------------------------ + void SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char *GetConditionText () const; + + //------------------------------------------------------------------ + // The next section are various utility functions. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Return the number of breakpoint locations that have resolved to + /// actual breakpoint sites. + /// + /// @return + /// The number locations resolved breakpoint sites. + //------------------------------------------------------------------ + size_t + GetNumResolvedLocations() const; + + //------------------------------------------------------------------ + /// Return the number of breakpoint locations. + /// + /// @return + /// The number breakpoint locations. + //------------------------------------------------------------------ + size_t + GetNumLocations() const; + + //------------------------------------------------------------------ + /// Put a description of this breakpoint into the stream \a s. + /// + /// @param[in] s + /// Stream into which to dump the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations = false); + + //------------------------------------------------------------------ + /// Set the "kind" description for a breakpoint. If the breakpoint is hit + /// the stop info will show this "kind" description instead of the breakpoint + /// number. Mostly useful for internal breakpoints, where the breakpoint number + /// doesn't have meaning to the user. + /// + /// @param[in] kind + /// New "kind" description. + //------------------------------------------------------------------ + void + SetBreakpointKind (const char *kind) + { + m_kind_description.assign (kind); + } + + //------------------------------------------------------------------ + /// Return the "kind" description for a breakpoint. + /// + /// @return + /// The breakpoint kind, or NULL if none is set. + //------------------------------------------------------------------ + const char *GetBreakpointKind () const + { + return m_kind_description.c_str(); + } + + //------------------------------------------------------------------ + /// Accessor for the breakpoint Target. + /// @return + /// This breakpoint's Target. + //------------------------------------------------------------------ + Target & + GetTarget (); + + const Target & + GetTarget () const; + + void + GetResolverDescription (Stream *s); + + //------------------------------------------------------------------ + /// Find breakpoint locations which match the (filename, line_number) description. + /// The breakpoint location collection is to be filled with the matching locations. + /// It should be initialized with 0 size by the API client. + /// + /// @return + /// True if there is a match + /// + /// The locations which match the filename and line_number in loc_coll. If its + /// size is 0 and true is returned, it means the breakpoint fully matches the + /// description. + //------------------------------------------------------------------ + bool GetMatchingFileLine(const ConstString &filename, uint32_t line_number, + BreakpointLocationCollection &loc_coll); + + void + GetFilterDescription (Stream *s); + + //------------------------------------------------------------------ + /// Returns the BreakpointOptions structure set at the breakpoint level. + /// + /// Meant to be used by the BreakpointLocation class. + /// + /// @return + /// A pointer to this breakpoint's BreakpointOptions. + //------------------------------------------------------------------ + BreakpointOptions * + GetOptions (); + + + //------------------------------------------------------------------ + /// Invoke the callback action when the breakpoint is hit. + /// + /// Meant to be used by the BreakpointLocation class. + /// + /// @param[in] context + /// Described the breakpoint event. + /// + /// @param[in] bp_loc_id + /// Which breakpoint location hit this breakpoint. + /// + /// @return + /// \b true if the target should stop at this breakpoint and \b false not. + //------------------------------------------------------------------ + bool + InvokeCallback (StoppointCallbackContext *context, + lldb::break_id_t bp_loc_id); + +protected: + friend class Target; + //------------------------------------------------------------------ + // Protected Methods + //------------------------------------------------------------------ + + + //------------------------------------------------------------------ + /// Constructors and Destructors + /// Only the Target can make a breakpoint, and it owns the breakpoint lifespans. + /// The constructor takes a filter and a resolver. Up in Target there are convenience + /// variants that make breakpoints for some common cases. + //------------------------------------------------------------------ + // This is the generic constructor + Breakpoint(Target &target, lldb::SearchFilterSP &filter_sp, lldb::BreakpointResolverSP &resolver_sp); + + friend class BreakpointLocation; // To call the following two when determining whether to stop. + + void + DecrementIgnoreCount(); + + // BreakpointLocation::IgnoreCountShouldStop & Breakpoint::IgnoreCountShouldStop can only be called once per stop, + // and BreakpointLocation::IgnoreCountShouldStop should be tested first, and if it returns false we should + // continue, otherwise we should test Breakpoint::IgnoreCountShouldStop. + + bool + IgnoreCountShouldStop (); + +private: + //------------------------------------------------------------------ + // For Breakpoint only + //------------------------------------------------------------------ + bool m_being_created; + Target &m_target; // The target that holds this breakpoint. + lldb::SearchFilterSP m_filter_sp; // The filter that constrains the breakpoint's domain. + lldb::BreakpointResolverSP m_resolver_sp; // The resolver that defines this breakpoint. + BreakpointOptions m_options; // Settable breakpoint options + BreakpointLocationList m_locations; // The list of locations currently found for this breakpoint. + std::string m_kind_description; + + void + SendBreakpointChangedEvent (lldb::BreakpointEventType eventKind); + + void + SendBreakpointChangedEvent (BreakpointEventData *data); + + DISALLOW_COPY_AND_ASSIGN(Breakpoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Breakpoint_h_ diff --git a/include/lldb/Breakpoint/BreakpointID.h b/include/lldb/Breakpoint/BreakpointID.h new file mode 100644 index 00000000000..9e352100b9e --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointID.h @@ -0,0 +1,117 @@ +//===-- BreakpointID.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointID_h_ +#define liblldb_BreakpointID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// class BreakpointID +//---------------------------------------------------------------------- + +class BreakpointID +{ +public: + + BreakpointID (lldb::break_id_t bp_id = LLDB_INVALID_BREAK_ID, + lldb::break_id_t loc_id = LLDB_INVALID_BREAK_ID); + + virtual + ~BreakpointID (); + + lldb::break_id_t + GetBreakpointID () + { + return m_break_id; + } + + lldb::break_id_t + GetLocationID () + { + return m_location_id; + } + + void + SetID (lldb::break_id_t bp_id, lldb::break_id_t loc_id) + { + m_break_id = bp_id; + m_location_id = loc_id; + } + + void + SetBreakpointID (lldb::break_id_t bp_id) + { + m_break_id = bp_id; + } + + void + SetBreakpointLocationID (lldb::break_id_t loc_id) + { + m_location_id = loc_id; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + static bool + IsRangeIdentifier (const char *str); + + static bool + IsValidIDExpression (const char *str); + + static const char *g_range_specifiers[]; + + //------------------------------------------------------------------ + /// Takes an input string containing the description of a breakpoint or breakpoint and location + /// and returns the breakpoint ID and the breakpoint location id. + /// + /// @param[in] input + /// A string containing JUST the breakpoint description. + /// @param[out] break_id + /// This is the break id. + /// @param[out] break_loc_id + /// This is breakpoint location id, or LLDB_INVALID_BREAK_ID is no location was specified. + /// @return + /// \b true if the call was able to extract a breakpoint location from the string. \b false otherwise. + //------------------------------------------------------------------ + static bool + ParseCanonicalReference (const char *input, lldb::break_id_t *break_id, lldb::break_id_t *break_loc_id); + + + //------------------------------------------------------------------ + /// Takes a breakpoint ID and the breakpoint location id and returns + /// a string containing the canonical description for the breakpoint + /// or breakpoint location. + /// + /// @param[out] break_id + /// This is the break id. + /// + /// @param[out] break_loc_id + /// This is breakpoint location id, or LLDB_INVALID_BREAK_ID is no + /// location is to be specified. + //------------------------------------------------------------------ + static void + GetCanonicalReference (Stream *s, lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + +protected: + lldb::break_id_t m_break_id; + lldb::break_id_t m_location_id; +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointID_h_ diff --git a/include/lldb/Breakpoint/BreakpointIDList.h b/include/lldb/Breakpoint/BreakpointIDList.h new file mode 100644 index 00000000000..c9fcef0a783 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointIDList.h @@ -0,0 +1,82 @@ +//===-- BreakpointIDList.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointIDList_h_ +#define liblldb_BreakpointIDList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/BreakpointID.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// class BreakpointIDList +//---------------------------------------------------------------------- + + +class BreakpointIDList +{ +public: + typedef std::vector BreakpointIDArray; + + BreakpointIDList (); + + virtual + ~BreakpointIDList (); + + size_t + GetSize(); + + BreakpointID & + GetBreakpointIDAtIndex (size_t index); + + bool + RemoveBreakpointIDAtIndex (size_t index); + + void + Clear(); + + bool + AddBreakpointID (BreakpointID bp_id); + + bool + AddBreakpointID (const char *bp_id); + + bool + FindBreakpointID (BreakpointID &bp_id, size_t *position); + + bool + FindBreakpointID (const char *bp_id, size_t *position); + + void + InsertStringArray (const char **string_array, size_t array_size, CommandReturnObject &result); + + static bool + StringContainsIDRangeExpression (const char *in_string, size_t *range_start_len, size_t *range_end_pos); + + static void + FindAndReplaceIDRanges (Args &old_args, Target *target, CommandReturnObject &result, Args &new_args); + +private: + BreakpointIDArray m_breakpoint_ids; + BreakpointID m_invalid_id; + + DISALLOW_COPY_AND_ASSIGN(BreakpointIDList); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointIDList_h_ diff --git a/include/lldb/Breakpoint/BreakpointList.h b/include/lldb/Breakpoint/BreakpointList.h new file mode 100644 index 00000000000..97eb2b46bc0 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointList.h @@ -0,0 +1,193 @@ +//===-- BreakpointList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointList_h_ +#define liblldb_BreakpointList_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointList BreakpointList.h "lldb/Breakpoint/BreakpointList.h" +/// @brief This class manages a list of breakpoints. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// Allows adding and removing breakpoints and find by ID and index. +//---------------------------------------------------------------------- + +class BreakpointList +{ +public: + BreakpointList (bool is_internal); + + ~BreakpointList(); + + //------------------------------------------------------------------ + /// Add the breakpoint \a bp_sp to the list. + /// + /// @param[in] bp_sp + /// Shared pointer to the breakpoint that will get added to the list. + /// + /// @result + /// Returns breakpoint id. + //------------------------------------------------------------------ + lldb::break_id_t + Add (lldb::BreakpointSP& bp_sp, bool notify); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with id \a breakID. + /// + /// @param[in] breakID + /// The breakpoint ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSP + FindBreakpointByID (lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with id \a breakID. Const version. + /// + /// @param[in] breakID + /// The breakpoint ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointSP + FindBreakpointByID (lldb::break_id_t breakID) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with index \a i. + /// + /// @param[in] i + /// The breakpoint index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSP + GetBreakpointAtIndex (size_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint with index \a i, const version + /// + /// @param[in] i + /// The breakpoint index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointSP + GetBreakpointAtIndex (size_t i) const; + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_breakpoints.size(); + } + + //------------------------------------------------------------------ + /// Removes the breakpoint given by \b breakID from this list. + /// + /// @param[in] breakID + /// The breakpoint index to remove. + /// + /// @result + /// \b true if the breakpoint \a breakID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::break_id_t breakID, bool notify); + + void + SetEnabledAll (bool enabled); + + //------------------------------------------------------------------ + /// Removes all the breakpoints from this list. + //------------------------------------------------------------------ + void + RemoveAll (bool notify); + + //------------------------------------------------------------------ + /// Tell all the breakpoints to update themselves due to a change in the + /// modules in \a module_list. \a added says whether the module was loaded + /// or unloaded. + /// + /// @param[in] module_list + /// The module list that has changed. + /// + /// @param[in] added + /// \b true if the modules are loaded, \b false if unloaded. + //------------------------------------------------------------------ + void + UpdateBreakpoints (ModuleList &module_list, bool added); + + void + UpdateBreakpointsWhenModuleIsReplaced (lldb::ModuleSP old_module_sp, lldb::ModuleSP new_module_sp); + + void + ClearAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Sets the passed in Locker to hold the Breakpoint List mutex. + /// + /// @param[in] locker + /// The locker object that is set. + //------------------------------------------------------------------ + void + GetListMutex (lldb_private::Mutex::Locker &locker); + +protected: + typedef std::list bp_collection; + + bp_collection::iterator + GetBreakpointIDIterator(lldb::break_id_t breakID); + + bp_collection::const_iterator + GetBreakpointIDConstIterator(lldb::break_id_t breakID) const; + + mutable Mutex m_mutex; + bp_collection m_breakpoints; // The breakpoint list, currently a list. + lldb::break_id_t m_next_break_id; + bool m_is_internal; + +private: + DISALLOW_COPY_AND_ASSIGN (BreakpointList); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointList_h_ diff --git a/include/lldb/Breakpoint/BreakpointLocation.h b/include/lldb/Breakpoint/BreakpointLocation.h new file mode 100644 index 00000000000..9ab0a79c684 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointLocation.h @@ -0,0 +1,401 @@ +//===-- BreakpointLocation.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointLocation_h_ +#define liblldb_BreakpointLocation_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/StoppointLocation.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/UserID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Expression/ClangUserExpression.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointLocation BreakpointLocation.h "lldb/Breakpoint/BreakpointLocation.h" +/// @brief Class that manages one unique (by address) instance of a logical breakpoint. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// A breakpoint location is defined by the breakpoint that produces it, +/// and the address that resulted in this particular instantiation. +/// Each breakpoint location also may have a breakpoint site if its +/// address has been loaded into the program. +/// Finally it has a settable options object. +/// +/// FIXME: Should we also store some fingerprint for the location, so +/// we can map one location to the "equivalent location" on rerun? This +/// would be useful if you've set options on the locations. +//---------------------------------------------------------------------- + +class BreakpointLocation : + public std::enable_shared_from_this, + public StoppointLocation +{ +public: + + ~BreakpointLocation (); + + //------------------------------------------------------------------ + /// Gets the load address for this breakpoint location + /// @return + /// Returns breakpoint location load address, \b + /// LLDB_INVALID_ADDRESS if not yet set. + //------------------------------------------------------------------ + lldb::addr_t + GetLoadAddress () const; + + //------------------------------------------------------------------ + /// Gets the Address for this breakpoint location + /// @return + /// Returns breakpoint location Address. + //------------------------------------------------------------------ + Address & + GetAddress (); + //------------------------------------------------------------------ + /// Gets the Breakpoint that created this breakpoint location + /// @return + /// Returns the owning breakpoint. + //------------------------------------------------------------------ + Breakpoint & + GetBreakpoint (); + + //------------------------------------------------------------------ + /// Determines whether we should stop due to a hit at this + /// breakpoint location. + /// + /// Side Effects: This may evaluate the breakpoint condition, and + /// run the callback. So this command may do a considerable amount + /// of work. + /// + /// @return + /// \b true if this breakpoint location thinks we should stop, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + // The next section deals with various breakpoint options. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false + /// disable it. + //------------------------------------------------------------------ + void + SetEnabled(bool enabled); + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled () const; + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + uint32_t + GetIgnoreCount (); + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + void + SetIgnoreCount (uint32_t n); + + //------------------------------------------------------------------ + /// Set the callback action invoked when the breakpoint is hit. + /// + /// The callback will return a bool indicating whether the target + /// should stop at this breakpoint or not. + /// + /// @param[in] callback + /// The method that will get called when the breakpoint is hit. + /// + /// @param[in] callback_baton_sp + /// A shared pointer to a Baton that provides the void * needed + /// for the callback. + /// + /// @see lldb_private::Baton + //------------------------------------------------------------------ + void + SetCallback (BreakpointHitCallback callback, + const lldb::BatonSP &callback_baton_sp, + bool is_synchronous); + + void + SetCallback (BreakpointHitCallback callback, + void *baton, + bool is_synchronous); + + void + ClearCallback (); + + //------------------------------------------------------------------ + /// Set the breakpoint location's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the breakpoint is hit. + //------------------------------------------------------------------ + void + SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char * + GetConditionText (size_t *hash = NULL) const; + + bool + ConditionSaysStop (ExecutionContext &exe_ctx, Error &error); + + + //------------------------------------------------------------------ + /// Set the valid thread to be checked when the breakpoint is hit. + /// + /// @param[in] thread_id + /// If this thread hits the breakpoint, we stop, otherwise not. + //------------------------------------------------------------------ + void + SetThreadID (lldb::tid_t thread_id); + + lldb::tid_t + GetThreadID (); + + void + SetThreadIndex (uint32_t index); + + uint32_t + GetThreadIndex() const; + + void + SetThreadName (const char *thread_name); + + const char * + GetThreadName () const; + + void + SetQueueName (const char *queue_name); + + const char * + GetQueueName () const; + + //------------------------------------------------------------------ + // The next section deals with this location's breakpoint sites. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Try to resolve the breakpoint site for this location. + /// + /// @return + /// \b true if we were successful at setting a breakpoint site, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + ResolveBreakpointSite (); + + //------------------------------------------------------------------ + /// Clear this breakpoint location's breakpoint site - for instance + /// when disabling the breakpoint. + /// + /// @return + /// \b true if there was a breakpoint site to be cleared, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + ClearBreakpointSite (); + + //------------------------------------------------------------------ + /// Return whether this breakpoint location has a breakpoint site. + /// @return + /// \b true if there was a breakpoint site for this breakpoint + /// location, \b false otherwise. + //------------------------------------------------------------------ + bool + IsResolved () const; + + lldb::BreakpointSiteSP + GetBreakpointSite() const; + + //------------------------------------------------------------------ + // The next section are generic report functions. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Print a description of this breakpoint location to the stream + /// \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Use this to set location specific breakpoint options. + /// + /// It will create a copy of the containing breakpoint's options if + /// that hasn't been done already + /// + /// @return + /// A pointer to the breakpoint options. + //------------------------------------------------------------------ + BreakpointOptions * + GetLocationOptions (); + + //------------------------------------------------------------------ + /// Use this to access breakpoint options from this breakpoint location. + /// This will point to the owning breakpoint's options unless options have + /// been set specifically on this location. + /// + /// @return + /// A pointer to the containing breakpoint's options if this + /// location doesn't have its own copy. + //------------------------------------------------------------------ + const BreakpointOptions * + GetOptionsNoCreate () const; + + bool + ValidForThisThread (Thread *thread); + + + //------------------------------------------------------------------ + /// Invoke the callback action when the breakpoint is hit. + /// + /// Meant to be used by the BreakpointLocation class. + /// + /// @param[in] context + /// Described the breakpoint event. + /// + /// @param[in] bp_loc_id + /// Which breakpoint location hit this breakpoint. + /// + /// @return + /// \b true if the target should stop at this breakpoint and \b + /// false not. + //------------------------------------------------------------------ + bool + InvokeCallback (StoppointCallbackContext *context); + +protected: + friend class BreakpointLocationList; + friend class CommandObjectBreakpointCommandAdd; + friend class Process; + + //------------------------------------------------------------------ + /// Set the breakpoint site for this location to \a bp_site_sp. + /// + /// @param[in] bp_site_sp + /// The breakpoint site we are setting for this location. + /// + /// @return + /// \b true if we were successful at setting the breakpoint site, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + SetBreakpointSite (lldb::BreakpointSiteSP& bp_site_sp); + + void + DecrementIgnoreCount(); + + bool + IgnoreCountShouldStop(); + +private: + + //------------------------------------------------------------------ + // Constructors and Destructors + // + // Only the Breakpoint can make breakpoint locations, and it owns + // them. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Constructor. + /// + /// @param[in] owner + /// A back pointer to the breakpoint that owns this location. + /// + /// @param[in] addr + /// The Address defining this location. + /// + /// @param[in] tid + /// The thread for which this breakpoint location is valid, or + /// LLDB_INVALID_THREAD_ID if it is valid for all threads. + /// + /// @param[in] hardware + /// \b true if a hardware breakpoint is requested. + //------------------------------------------------------------------ + + BreakpointLocation (lldb::break_id_t bid, + Breakpoint &owner, + const Address &addr, + lldb::tid_t tid = LLDB_INVALID_THREAD_ID, + bool hardware = false); + + //------------------------------------------------------------------ + // Data members: + //------------------------------------------------------------------ + bool m_being_created; + Address m_address; ///< The address defining this location. + Breakpoint &m_owner; ///< The breakpoint that produced this object. + std::unique_ptr m_options_ap; ///< Breakpoint options pointer, NULL if we're using our breakpoint's options. + lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be shared by more than one location.) + ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; ///< The compiled expression to use in testing our condition. + Mutex m_condition_mutex; ///< Guards parsing and evaluation of the condition, which could be evaluated by multiple processes. + size_t m_condition_hash; ///< For testing whether the condition source code changed. + + void + SendBreakpointLocationChangedEvent (lldb::BreakpointEventType eventKind); + + DISALLOW_COPY_AND_ASSIGN (BreakpointLocation); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocation_h_ diff --git a/include/lldb/Breakpoint/BreakpointLocationCollection.h b/include/lldb/Breakpoint/BreakpointLocationCollection.h new file mode 100644 index 00000000000..7f6a659323b --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointLocationCollection.h @@ -0,0 +1,209 @@ +//===-- BreakpointLocationCollection.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointLocationCollection_h_ +#define liblldb_BreakpointLocationCollection_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class BreakpointLocationCollection +{ +public: + BreakpointLocationCollection(); + + ~BreakpointLocationCollection(); + + //------------------------------------------------------------------ + /// Add the breakpoint \a bp_loc_sp to the list. + /// + /// @param[in] bp_sp + /// Shared pointer to the breakpoint location that will get added + /// to the list. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + void + Add (const lldb::BreakpointLocationSP& bp_loc_sp); + + //------------------------------------------------------------------ + /// Removes the breakpoint location given by \b breakID from this + /// list. + /// + /// @param[in] break_id + /// The breakpoint index to remove. + /// + /// @param[in] break_loc_id + /// The breakpoint location index in break_id to remove. + /// + /// @result + /// \b true if the breakpoint was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id \a + /// breakID. + /// + /// @param[in] break_id + /// The breakpoint ID to seek for. + /// + /// @param[in] break_loc_id + /// The breakpoint location ID in \a break_id to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id \a + /// breakID, const version. + /// + /// @param[in] breakID + /// The breakpoint location ID to seek for. + /// + /// @param[in] break_loc_id + /// The breakpoint location ID in \a break_id to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + FindByIDPair (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with index + /// \a i. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetByIndex (size_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with index + /// \a i, const version. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + GetByIndex (size_t i) const; + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint location list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const { return m_break_loc_collection.size(); } + + //------------------------------------------------------------------ + /// Enquires of all the breakpoint locations in this list whether + /// we should stop at a hit at \a breakID. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] breakID + /// This break ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + /// Print a description of the breakpoint locations in this list + /// to the stream \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void GetDescription (Stream *s, lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Check whether this collection of breakpoint locations have any + /// thread specifiers, and if yes, is \a thread_id contained in any + /// of these specifiers. + /// + /// @param[in] thread + /// The thread against which to test. + /// + /// return + /// \b true if the collection contains at least one location that + /// would be valid for this thread, false otherwise. + //------------------------------------------------------------------ + bool ValidForThisThread (Thread *thread); + + //------------------------------------------------------------------ + /// Tell whether ALL the breakpoints in the location collection are internal. + /// + /// @result + /// \b true if all breakpoint locations are owned by internal breakpoints, + /// \b false otherwise. + //------------------------------------------------------------------ + bool IsInternal() const; + + +protected: + //------------------------------------------------------------------ + // Classes that inherit from BreakpointLocationCollection can see + // and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For BreakpointLocationCollection only + //------------------------------------------------------------------ + + typedef std::vector collection; + + collection::iterator + GetIDPairIterator(lldb::break_id_t break_id, lldb::break_id_t break_loc_id); + + collection::const_iterator + GetIDPairConstIterator(lldb::break_id_t break_id, lldb::break_id_t break_loc_id) const; + + collection m_break_loc_collection; + +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocationCollection_h_ diff --git a/include/lldb/Breakpoint/BreakpointLocationList.h b/include/lldb/Breakpoint/BreakpointLocationList.h new file mode 100644 index 00000000000..1cba23d9118 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointLocationList.h @@ -0,0 +1,269 @@ +//===-- BreakpointLocationList.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointLocationList_h_ +#define liblldb_BreakpointLocationList_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointLocationList BreakpointLocationList.h "lldb/Breakpoint/BreakpointLocationList.h" +/// @brief This class is used by Breakpoint to manage a list of breakpoint locations, +// each breakpoint location in the list +/// has a unique ID, and is unique by Address as well. +//---------------------------------------------------------------------- + +class BreakpointLocationList +{ +// Only Breakpoints can make the location list, or add elements to it. +// This is not just some random collection of locations. Rather, the act of adding the location +// to this list sets its ID, and implicitly all the locations have the same breakpoint ID as +// well. If you need a generic container for breakpoint locations, use BreakpointLocationCollection. +friend class Breakpoint; + +public: + virtual + ~BreakpointLocationList(); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location at address + /// \a addr - const version. + /// + /// @param[in] addr + /// The address to look for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + FindByAddress (const Address &addr) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with id + /// \a breakID, const version. + /// + /// @param[in] breakID + /// The breakpoint location ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + FindByID (lldb::break_id_t breakID) const; + + //------------------------------------------------------------------ + /// Returns the breakpoint location id to the breakpoint location + /// at address \a addr. + /// + /// @param[in] addr + /// The address to match. + /// + /// @result + /// The ID of the breakpoint location, or LLDB_INVALID_BREAK_ID. + //------------------------------------------------------------------ + lldb::break_id_t + FindIDByAddress (const Address &addr); + + //------------------------------------------------------------------ + /// Returns a breakpoint location list of the breakpoint locations + /// in the module \a module. This list is allocated, and owned by + /// the caller. + /// + /// @param[in] module + /// The module to seek in. + /// + /// @param[in] + /// A breakpoint collection that gets any breakpoint locations + /// that match \a module appended to. + /// + /// @result + /// The number of matches + //------------------------------------------------------------------ + size_t + FindInModule (Module *module, + BreakpointLocationCollection& bp_loc_list); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with + /// index \a i. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetByIndex (size_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint location with index + /// \a i, const version. + /// + /// @param[in] i + /// The breakpoint location index to seek for. + /// + /// @result + /// A shared pointer to the breakpoint. May contain a NULL + /// pointer if the breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointLocationSP + GetByIndex (size_t i) const; + + //------------------------------------------------------------------ + /// Removes all the locations in this list from their breakpoint site + /// owners list. + //------------------------------------------------------------------ + void + ClearAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Tells all the breakopint locations in this list to attempt to + /// resolve any possible breakpoint sites. + //------------------------------------------------------------------ + void + ResolveAllBreakpointSites (); + + //------------------------------------------------------------------ + /// Returns the number of breakpoint locations in this list with + /// resolved breakpoints. + /// + /// @result + /// Number of qualifying breakpoint locations. + //------------------------------------------------------------------ + size_t + GetNumResolvedLocations() const; + + //------------------------------------------------------------------ + /// Returns the number hit count of all locations in this list. + /// + /// @result + /// Hit count of all locations in this list. + //------------------------------------------------------------------ + uint32_t + GetHitCount () const; + + //------------------------------------------------------------------ + /// Enquires of the breakpoint location in this list with ID \a + /// breakID whether we should stop. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] breakID + /// This break ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context, + lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns the number of elements in this breakpoint location list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + return m_locations.size(); + } + + //------------------------------------------------------------------ + /// Print a description of the breakpoint locations in this list to + /// the stream \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + +protected: + + //------------------------------------------------------------------ + /// This is the standard constructor. + /// + /// It creates an empty breakpoint location list. It is protected + /// here because only Breakpoints are allowed to create the + /// breakpoint location list. + //------------------------------------------------------------------ + BreakpointLocationList(Breakpoint &owner); + + //------------------------------------------------------------------ + /// Add the breakpoint \a bp_loc_sp to the list. + /// + /// @param[in] bp_sp + /// Shared pointer to the breakpoint location that will get + /// added to the list. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + Create (const Address &addr); + + void + StartRecordingNewLocations(BreakpointLocationCollection &new_locations); + + void + StopRecordingNewLocations(); + + lldb::BreakpointLocationSP + AddLocation (const Address &addr, + bool *new_location = NULL); + + bool + RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp); + + typedef std::vector collection; + typedef std::map addr_map; + + Breakpoint &m_owner; + collection m_locations; + addr_map m_address_to_location; + mutable Mutex m_mutex; + lldb::break_id_t m_next_id; + BreakpointLocationCollection *m_new_location_recorder; +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointLocationList_h_ diff --git a/include/lldb/Breakpoint/BreakpointOptions.h b/include/lldb/Breakpoint/BreakpointOptions.h new file mode 100644 index 00000000000..728f5932fa0 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointOptions.h @@ -0,0 +1,358 @@ +//===-- BreakpointOptions.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointOptions_h_ +#define liblldb_BreakpointOptions_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Baton.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointOptions BreakpointOptions.h "lldb/Breakpoint/BreakpointOptions.h" +/// @brief Class that manages the options on a breakpoint or breakpoint location. +//---------------------------------------------------------------------- + +class BreakpointOptions +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Default constructor. The breakpoint is enabled, and has no condition, + /// callback, ignore count, etc... + //------------------------------------------------------------------ + BreakpointOptions(); + BreakpointOptions(const BreakpointOptions& rhs); + + static BreakpointOptions * + CopyOptionsNoCallback (BreakpointOptions &rhs); + //------------------------------------------------------------------ + /// This constructor allows you to specify all the breakpoint options. + /// + /// @param[in] condition + /// The expression which if it evaluates to \b true if we are to stop + /// + /// @param[in] callback + /// This is the plugin for some code that gets run, returns \b true if we are to stop. + /// + /// @param[in] baton + /// Client data that will get passed to the callback. + /// + /// @param[in] enabled + /// Is this breakpoint enabled. + /// + /// @param[in] ignore + /// How many breakpoint hits we should ignore before stopping. + /// + /// @param[in] thread_id + /// Only stop if \a thread_id hits the breakpoint. + //------------------------------------------------------------------ + BreakpointOptions(void *condition, + BreakpointHitCallback callback, + void *baton, + bool enabled = true, + int32_t ignore = 0, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID, + bool one_shot = false); + + virtual ~BreakpointOptions(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const BreakpointOptions& + operator=(const BreakpointOptions& rhs); + + //------------------------------------------------------------------ + // Callbacks + // + // Breakpoint callbacks come in two forms, synchronous and asynchronous. Synchronous callbacks will get + // run before any of the thread plans are consulted, and if they return false the target will continue + // "under the radar" of the thread plans. There are a couple of restrictions to synchronous callbacks: + // 1) They should NOT resume the target themselves. Just return false if you want the target to restart. + // 2) Breakpoints with synchronous callbacks can't have conditions (or rather, they can have them, but they + // won't do anything. Ditto with ignore counts, etc... You are supposed to control that all through the + // callback. + // Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan. The logic there is: + // a) If the breakpoint is thread specific and not for this thread, continue w/o running the callback. + // b) If the ignore count says we shouldn't stop, then ditto. + // c) If the condition says we shouldn't stop, then ditto. + // d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't. + // The asynchronous callback can run the target itself, but at present that should be the last action the + // callback does. We will relax this condition at some point, but it will take a bit of plumbing to get + // that to work. + // + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Adds a callback to the breakpoint option set. + /// + /// @param[in] callback + /// The function to be called when the breakpoint gets hit. + /// + /// @param[in] baton_sp + /// A baton which will get passed back to the callback when it is invoked. + /// + /// @param[in] synchronous + /// Whether this is a synchronous or asynchronous callback. See discussion above. + //------------------------------------------------------------------ + void SetCallback (BreakpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false); + + + //------------------------------------------------------------------ + /// Remove the callback from this option set. + //------------------------------------------------------------------ + void ClearCallback (); + + // The rest of these functions are meant to be used only within the breakpoint handling mechanism. + + //------------------------------------------------------------------ + /// Use this function to invoke the callback for a specific stop. + /// + /// @param[in] context + /// The context in which the callback is to be invoked. This includes the stop event, the + /// execution context of the stop (since you might hit the same breakpoint on multiple threads) and + /// whether we are currently executing synchronous or asynchronous callbacks. + /// + /// @param[in] break_id + /// The breakpoint ID that owns this option set. + /// + /// @param[in] break_loc_id + /// The breakpoint location ID that owns this option set. + /// + /// @return + /// The callback return value. + //------------------------------------------------------------------ + bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + + //------------------------------------------------------------------ + /// Used in InvokeCallback to tell whether it is the right time to run this kind of callback. + /// + /// @return + /// The synchronicity of our callback. + //------------------------------------------------------------------ + bool IsCallbackSynchronous () { + return m_callback_is_synchronous; + } + + //------------------------------------------------------------------ + /// Fetch the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + Baton *GetBaton (); + + //------------------------------------------------------------------ + /// Fetch a const version of the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + const Baton *GetBaton () const; + + //------------------------------------------------------------------ + // Condition + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Set the breakpoint option's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the breakpoint is hit. + //------------------------------------------------------------------ + void SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char *GetConditionText (size_t *hash = NULL) const; + + //------------------------------------------------------------------ + // Enabled/Ignore Count + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Check the Enable/Disable state. + /// @return + /// \b true if the breakpoint is enabled, \b false if disabled. + //------------------------------------------------------------------ + bool + IsEnabled () const + { + return m_enabled; + } + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetEnabled (bool enabled) + { + m_enabled = enabled; + } + + //------------------------------------------------------------------ + /// Check the One-shot state. + /// @return + /// \b true if the breakpoint is one-shot, \b false otherwise. + //------------------------------------------------------------------ + bool + IsOneShot () const + { + return m_one_shot; + } + + //------------------------------------------------------------------ + /// If \a enable is \b true, enable the breakpoint, if \b false disable it. + //------------------------------------------------------------------ + void + SetOneShot (bool one_shot) + { + m_one_shot = one_shot; + } + + //------------------------------------------------------------------ + /// Set the breakpoint to ignore the next \a count breakpoint hits. + /// @param[in] count + /// The number of breakpoint hits to ignore. + //------------------------------------------------------------------ + + void + SetIgnoreCount (uint32_t n) + { + m_ignore_count = n; + } + + //------------------------------------------------------------------ + /// Return the current Ignore Count. + /// @return + /// The number of breakpoint hits to be ignored. + //------------------------------------------------------------------ + uint32_t + GetIgnoreCount () const + { + return m_ignore_count; + } + + //------------------------------------------------------------------ + /// Return the current thread spec for this option. This will return NULL if the no thread + /// specifications have been set for this Option yet. + /// @return + /// The thread specification pointer for this option, or NULL if none has + /// been set yet. + //------------------------------------------------------------------ + const ThreadSpec * + GetThreadSpecNoCreate () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the ThreadSpec for this option, creating it. + /// if it hasn't been created already. This API is used for setting the + /// ThreadSpec items for this option. + //------------------------------------------------------------------ + ThreadSpec * + GetThreadSpec (); + + void + SetThreadID(lldb::tid_t thread_id); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Returns true if the breakpoint option has a callback set. + //------------------------------------------------------------------ + bool + HasCallback(); + + //------------------------------------------------------------------ + /// This is the default empty callback. + /// @return + /// The thread id for which the breakpoint hit will stop, + /// LLDB_INVALID_THREAD_ID for all threads. + //------------------------------------------------------------------ + static bool + NullCallback (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + + struct CommandData + { + CommandData () : + user_source(), + script_source(), + stop_on_error(true) + { + } + + ~CommandData () + { + } + + StringList user_source; + std::string script_source; + bool stop_on_error; + }; + + class CommandBaton : public Baton + { + public: + CommandBaton (CommandData *data) : + Baton (data) + { + } + + virtual + ~CommandBaton () + { + delete ((CommandData *)m_data); + m_data = NULL; + } + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + }; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from BreakpointOptions can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For BreakpointOptions only + //------------------------------------------------------------------ + BreakpointHitCallback m_callback; // This is the callback function pointer + lldb::BatonSP m_callback_baton_sp; // This is the client data for the callback + bool m_callback_is_synchronous; + bool m_enabled; + bool m_one_shot; + uint32_t m_ignore_count; // Number of times to ignore this breakpoint + std::unique_ptr m_thread_spec_ap; // Thread for which this breakpoint will take + std::string m_condition_text; // The condition to test. + size_t m_condition_text_hash; // Its hash, so that locations know when the condition is updated. +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointOptions_h_ diff --git a/include/lldb/Breakpoint/BreakpointResolver.h b/include/lldb/Breakpoint/BreakpointResolver.h new file mode 100644 index 00000000000..3db3795453e --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointResolver.h @@ -0,0 +1,147 @@ +//===-- BreakpointResolver.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolver_h_ +#define liblldb_BreakpointResolver_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/ConstString.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolver BreakpointResolver.h "lldb/Breakpoint/BreakpointResolver.h" +/// @brief This class works with SearchFilter to resolve logical breakpoints to their +/// of concrete breakpoint locations. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// The BreakpointResolver is a Searcher. In that protocol, +/// the SearchFilter asks the question "At what depth of the symbol context +/// descent do you want your callback to get called?" of the filter. The resolver +/// answers this question (in the GetDepth method) and provides the resolution callback. +/// Each Breakpoint has a BreakpointResolver, and it calls either ResolveBreakpoint +/// or ResolveBreakpointInModules to tell it to look for new breakpoint locations. +//---------------------------------------------------------------------- + +class BreakpointResolver : + public Searcher +{ +public: + //------------------------------------------------------------------ + /// The breakpoint resolver need to have a breakpoint for "ResolveBreakpoint + /// to make sense. It can be constructed without a breakpoint, but you have to + /// call SetBreakpoint before ResolveBreakpoint. + /// + /// @param[in] bkpt + /// The breakpoint that owns this resolver. + /// @param[in] resolverType + /// The concrete breakpoint resolver type for this breakpoint. + /// + /// @result + /// Returns breakpoint location id. + //------------------------------------------------------------------ + BreakpointResolver (Breakpoint *bkpt, unsigned char resolverType); + + //------------------------------------------------------------------ + /// The Destructor is virtual, all significant breakpoint resolvers derive + /// from this class. + //------------------------------------------------------------------ + virtual + ~BreakpointResolver (); + + //------------------------------------------------------------------ + /// This sets the breakpoint for this resolver. + /// + /// @param[in] bkpt + /// The breakpoint that owns this resolver. + //------------------------------------------------------------------ + void + SetBreakpoint (Breakpoint *bkpt); + + //------------------------------------------------------------------ + /// In response to this method the resolver scans all the modules in the breakpoint's + /// target, and adds any new locations it finds. + /// + /// @param[in] filter + /// The filter that will manage the search for this resolver. + //------------------------------------------------------------------ + virtual void + ResolveBreakpoint (SearchFilter &filter); + + //------------------------------------------------------------------ + /// In response to this method the resolver scans the modules in the module list + /// \a modules, and adds any new locations it finds. + /// + /// @param[in] filter + /// The filter that will manage the search for this resolver. + //------------------------------------------------------------------ + virtual void + ResolveBreakpointInModules (SearchFilter &filter, + ModuleList &modules); + + //------------------------------------------------------------------ + /// Prints a canonical description for the breakpoint to the stream \a s. + /// + /// @param[in] s + /// Stream to which the output is copied. + //------------------------------------------------------------------ + virtual void + GetDescription (Stream *s) = 0; + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) const = 0; + + //------------------------------------------------------------------ + /// An enumeration for keeping track of the concrete subclass that + /// is actually instantiated. Values of this enumeration are kept in the + /// BreakpointResolver's SubclassID field. They are used for concrete type + /// identification. + enum ResolverTy { + FileLineResolver, // This is an instance of BreakpointResolverFileLine + AddressResolver, // This is an instance of BreakpointResolverAddress + NameResolver, // This is an instance of BreakpointResolverName + FileRegexResolver, + ExceptionResolver + }; + + //------------------------------------------------------------------ + /// getResolverID - Return an ID for the concrete type of this object. This + /// is used to implement the LLVM classof checks. This should not be used + /// for any other purpose, as the values may change as LLDB evolves. + unsigned getResolverID() const { + return SubclassID; + } + +protected: + Breakpoint *m_breakpoint; // This is the breakpoint we add locations to. + +private: + // Subclass identifier (for llvm isa/dyn_cast) + const unsigned char SubclassID; + DISALLOW_COPY_AND_ASSIGN(BreakpointResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolver_h_ diff --git a/include/lldb/Breakpoint/BreakpointResolverAddress.h b/include/lldb/Breakpoint/BreakpointResolverAddress.h new file mode 100644 index 00000000000..4ca4a405957 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointResolverAddress.h @@ -0,0 +1,74 @@ +//===-- BreakpointResolverAddress.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverAddress_h_ +#define liblldb_BreakpointResolverAddress_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverAddress BreakpointResolverAddress.h "lldb/Breakpoint/BreakpointResolverAddress.h" +/// @brief This class sets breakpoints on a given Address. This breakpoint only takes +/// once, and then it won't attempt to reset itself. +//---------------------------------------------------------------------- + +class BreakpointResolverAddress: + public BreakpointResolver +{ +public: + BreakpointResolverAddress (Breakpoint *bkpt, + const Address &addr); + + virtual + ~BreakpointResolverAddress (); + + virtual void + ResolveBreakpoint (SearchFilter &filter); + + virtual void + ResolveBreakpointInModules (SearchFilter &filter, + ModuleList &modules); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverAddress *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::AddressResolver; + } + +protected: + Address m_addr; + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverAddress); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverAddress_h_ diff --git a/include/lldb/Breakpoint/BreakpointResolverFileLine.h b/include/lldb/Breakpoint/BreakpointResolverFileLine.h new file mode 100644 index 00000000000..cc1633ce170 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointResolverFileLine.h @@ -0,0 +1,74 @@ +//===-- BreakpointResolverFileLine.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverFileLine_h_ +#define liblldb_BreakpointResolverFileLine_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverFileLine BreakpointResolverFileLine.h "lldb/Breakpoint/BreakpointResolverFileLine.h" +/// @brief This class sets breakpoints by file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class BreakpointResolverFileLine : + public BreakpointResolver +{ +public: + BreakpointResolverFileLine (Breakpoint *bkpt, + const FileSpec &resolver, + uint32_t line_no, + bool check_inlines, + bool skip_prologue); + + virtual + ~BreakpointResolverFileLine (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverFileLine *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::FileLineResolver; + } + +protected: + friend class Breakpoint; + FileSpec m_file_spec; // This is the file spec we are looking for. + uint32_t m_line_number; // This is the line number that we are looking for. + bool m_inlines; // This determines whether the resolver looks for inlined functions or not. + bool m_skip_prologue; + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverFileLine); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverFileLine_h_ diff --git a/include/lldb/Breakpoint/BreakpointResolverFileRegex.h b/include/lldb/Breakpoint/BreakpointResolverFileRegex.h new file mode 100644 index 00000000000..f1c2b1409e9 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointResolverFileRegex.h @@ -0,0 +1,68 @@ +//===-- BreakpointResolverFileRegex.h ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverFileRegex_h_ +#define liblldb_BreakpointResolverFileRegex_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverFileRegex BreakpointResolverFileRegex.h "lldb/Breakpoint/BreakpointResolverFileRegex.h" +/// @brief This class sets breakpoints by file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class BreakpointResolverFileRegex : + public BreakpointResolver +{ +public: + BreakpointResolverFileRegex (Breakpoint *bkpt, + RegularExpression ®ex); + + virtual + ~BreakpointResolverFileRegex (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// Methods for support type inquiry through isa, cast, and dyn_cast: + static inline bool classof(const BreakpointResolverFileRegex *) { return true; } + static inline bool classof(const BreakpointResolver *V) { + return V->getResolverID() == BreakpointResolver::FileRegexResolver; + } + +protected: + friend class Breakpoint; + RegularExpression m_regex; // This is the line expression that we are looking for. + +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverFileRegex); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverFileRegex_h_ diff --git a/include/lldb/Breakpoint/BreakpointResolverName.h b/include/lldb/Breakpoint/BreakpointResolverName.h new file mode 100644 index 00000000000..f481aa9c533 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointResolverName.h @@ -0,0 +1,122 @@ +//===-- BreakpointResolverName.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointResolverName_h_ +#define liblldb_BreakpointResolverName_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointResolverName BreakpointResolverName.h "lldb/Breakpoint/BreakpointResolverName.h" +/// @brief This class sets breakpoints on a given function name, either by exact match +/// or by regular expression. +//---------------------------------------------------------------------- + +class BreakpointResolverName: + public BreakpointResolver +{ +public: + + BreakpointResolverName (Breakpoint *bkpt, + const char *name, + uint32_t name_type_mask, + Breakpoint::MatchType type, + bool skip_prologue); + + // This one takes an array of names. It is always MatchType = Exact. + BreakpointResolverName (Breakpoint *bkpt, + const char *names[], + size_t num_names, + uint32_t name_type_mask, + bool skip_prologue); + + // This one takes a C++ array of names. It is always MatchType = Exact. + BreakpointResolverName (Breakpoint *bkpt, + std::vector names, + uint32_t name_type_mask, + bool skip_prologue); + + // Creates a function breakpoint by regular expression. Takes over control of the lifespan of func_regex. + BreakpointResolverName (Breakpoint *bkpt, + RegularExpression &func_regex, + bool skip_prologue); + + BreakpointResolverName (Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type, + bool skip_prologue); + + virtual + ~BreakpointResolverName (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + virtual void + Dump (Stream *s) const; + + /// 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::NameResolver; + } + +protected: + struct LookupInfo + { + ConstString name; + ConstString lookup_name; + uint32_t name_type_mask; // See FunctionNameType + bool match_name_after_lookup; + + LookupInfo () : + name(), + lookup_name(), + name_type_mask (0), + match_name_after_lookup (false) + { + } + + void + Prune (SymbolContextList &sc_list, + size_t start_idx) const; + }; + std::vector m_lookups; + ConstString m_class_name; + RegularExpression m_regex; + Breakpoint::MatchType m_match_type; + bool m_skip_prologue; + + void + AddNameLookup (const ConstString &name, uint32_t name_type_mask); +private: + DISALLOW_COPY_AND_ASSIGN(BreakpointResolverName); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointResolverName_h_ diff --git a/include/lldb/Breakpoint/BreakpointSite.h b/include/lldb/Breakpoint/BreakpointSite.h new file mode 100644 index 00000000000..271a23c2e45 --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointSite.h @@ -0,0 +1,295 @@ +//===-- BreakpointSite.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointSite_h_ +#define liblldb_BreakpointSite_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +#include "lldb/Breakpoint/StoppointLocation.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointSite BreakpointSite.h "lldb/Breakpoint/BreakpointSite.h" +/// @brief Class that manages the actual breakpoint that will be inserted +/// into the running program. +/// +/// The BreakpointSite class handles the physical breakpoint that is +/// actually inserted in the target program. As such, it is also the +/// one that gets hit, when the program stops. It keeps a list of all +/// BreakpointLocations that share this phsyical site. When the +/// breakpoint is hit, all the locations are informed by the breakpoint +/// site. Breakpoint sites are owned by the process. +//---------------------------------------------------------------------- + +class BreakpointSite : + public std::enable_shared_from_this, + public StoppointLocation +{ +public: + + enum Type + { + eSoftware, // Breakpoint opcode has been written to memory and m_saved_opcode + // and m_trap_opcode contain the saved and written opcode. + eHardware, // Breakpoint site is set as a hardware breakpoint + eExternal // Breakpoint site is managed by an external debug nub or + // debug interface where memory reads trasparently will not + // display any breakpoint opcodes. + }; + + virtual ~BreakpointSite (); + + //---------------------------------------------------------------------- + // This section manages the breakpoint traps + //---------------------------------------------------------------------- + + //------------------------------------------------------------------ + /// Returns the Opcode Bytes for this breakpoint + //------------------------------------------------------------------ + uint8_t * + GetTrapOpcodeBytes (); + + //------------------------------------------------------------------ + /// Returns the Opcode Bytes for this breakpoint - const version + //------------------------------------------------------------------ + const uint8_t * + GetTrapOpcodeBytes () const; + + //------------------------------------------------------------------ + /// Get the size of the trap opcode for this address + //------------------------------------------------------------------ + size_t + GetTrapOpcodeMaxByteSize () const; + + //------------------------------------------------------------------ + /// Sets the trap opcode + //------------------------------------------------------------------ + bool + SetTrapOpcode (const uint8_t *trap_opcode, + uint32_t trap_opcode_size); + + //------------------------------------------------------------------ + /// Gets the original instruction bytes that were overwritten by the trap + //------------------------------------------------------------------ + uint8_t * + GetSavedOpcodeBytes (); + + //------------------------------------------------------------------ + /// Gets the original instruction bytes that were overwritten by the trap const version + //------------------------------------------------------------------ + const uint8_t * + GetSavedOpcodeBytes () const; + + //------------------------------------------------------------------ + /// Says whether \a addr and size \a size intersects with the address \a intersect_addr + //------------------------------------------------------------------ + bool + IntersectsRange (lldb::addr_t addr, + size_t size, + lldb::addr_t *intersect_addr, + size_t *intersect_size, + size_t *opcode_offset) const; + + //------------------------------------------------------------------ + /// Tells whether the current breakpoint site is enabled or not + /// + /// This is a low-level enable bit for the breakpoint sites. If a + /// breakpoint site has no enabled owners, it should just get + /// removed. This enable/disable is for the low-level target code + /// to enable and disable breakpoint sites when single stepping, + /// etc. + //------------------------------------------------------------------ + bool + IsEnabled () const; + + //------------------------------------------------------------------ + /// Sets whether the current breakpoint site is enabled or not + /// + /// @param[in] enabled + /// \b true if the breakoint is enabled, \b false otherwise. + //------------------------------------------------------------------ + void + SetEnabled (bool enabled); + + //------------------------------------------------------------------ + /// Enquires of the breakpoint locations that produced this breakpoint site whether + /// we should stop at this location. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ShouldStop (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + /// Standard Dump method + /// + /// @param[in] context + /// The stream to dump this output. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// The "Owners" are the breakpoint locations that share this + /// breakpoint site. The method adds the \a owner to this breakpoint + /// site's owner list. + /// + /// @param[in] context + /// \a owner is the Breakpoint Location to add. + //------------------------------------------------------------------ + void + AddOwner (const lldb::BreakpointLocationSP &owner); + + //------------------------------------------------------------------ + /// This method returns the number of breakpoint locations currently + /// located at this breakpoint site. + /// + /// @return + /// The number of owners. + //------------------------------------------------------------------ + size_t + GetNumberOfOwners (); + + //------------------------------------------------------------------ + /// This method returns the the breakpoint location at index \a index + /// located at this breakpoint site. The owners are listed ordinally + /// from 0 to GetNumberOfOwners() - 1 so you can use this method to iterate + /// over the owners + /// + /// @param[in] index + /// The index in the list of owners for which you wish the owner location. + /// @return + /// A shared pointer to the breakpoint location at that index. + //------------------------------------------------------------------ + lldb::BreakpointLocationSP + GetOwnerAtIndex (size_t idx); + + //------------------------------------------------------------------ + /// Check whether the owners of this breakpoint site have any + /// thread specifiers, and if yes, is \a thread contained in any + /// of these specifiers. + /// + /// @param[in] thread + /// The thread against which to test. + /// + /// return + /// \b true if the collection contains at least one location that + /// would be valid for this thread, false otherwise. + //------------------------------------------------------------------ + bool + ValidForThisThread (Thread *thread); + + + //------------------------------------------------------------------ + /// Print a description of this breakpoint site to the stream \a s. + /// GetDescription tells you about the breakpoint site's owners. + /// Use BreakpointSite::Dump(Stream *) to get information about the + /// breakpoint site itself. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Tell whether a breakpoint has a location at this site. + /// + /// @param[in] bp_id + /// The breakpoint id to query. + /// + /// @result + /// \b true if bp_id has a location that is at this site, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + IsBreakpointAtThisSite (lldb::break_id_t bp_id); + + //------------------------------------------------------------------ + /// Tell whether ALL the breakpoints in the location collection are internal. + /// + /// @result + /// \b true if all breakpoint locations are owned by internal breakpoints, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + IsInternal () const; + + BreakpointSite::Type + GetType () const + { + return m_type; + } + + void + SetType (BreakpointSite::Type type) + { + m_type = type; + } + +private: + friend class Process; + + //------------------------------------------------------------------ + /// The method removes the owner at \a break_loc_id from this breakpoint list. + /// + /// @param[in] context + /// \a break_loc_id is the Breakpoint Location to remove. + //------------------------------------------------------------------ + size_t + RemoveOwner (lldb::break_id_t break_id, + lldb::break_id_t break_loc_id); + + BreakpointSite::Type m_type;///< The type of this breakpoint site. + uint8_t m_saved_opcode[8]; ///< The saved opcode bytes if this breakpoint site uses trap opcodes. + uint8_t m_trap_opcode[8]; ///< The opcode that was used to create the breakpoint if it is a software breakpoint site. + bool m_enabled; ///< Boolean indicating if this breakpoint site enabled or not. + + // Consider adding an optimization where if there is only one + // owner, we don't store a list. The usual case will be only one owner... + BreakpointLocationCollection m_owners; ///< This has the BreakpointLocations that share this breakpoint site. + + static lldb::break_id_t + GetNextID(); + + // Only the Process can create breakpoint sites in + // Process::CreateBreakpointSite (lldb::BreakpointLocationSP &, bool). + BreakpointSite (BreakpointSiteList *list, + const lldb::BreakpointLocationSP& owner, + lldb::addr_t m_addr, + bool use_hardware); + + DISALLOW_COPY_AND_ASSIGN(BreakpointSite); +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointSite_h_ diff --git a/include/lldb/Breakpoint/BreakpointSiteList.h b/include/lldb/Breakpoint/BreakpointSiteList.h new file mode 100644 index 00000000000..0d4dafc4baa --- /dev/null +++ b/include/lldb/Breakpoint/BreakpointSiteList.h @@ -0,0 +1,216 @@ +//===-- BreakpointSiteList.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_BreakpointSiteList_h_ +#define liblldb_BreakpointSiteList_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class BreakpointSiteList BreakpointSiteList.h "lldb/Breakpoint/BreakpointSiteList.h" +/// @brief Class that manages lists of BreakpointSite shared pointers. +//---------------------------------------------------------------------- +class BreakpointSiteList +{ +// At present Process directly accesses the map of BreakpointSites so it can +// do quick lookups into the map (using GetMap). +// FIXME: Find a better interface for this. +friend class Process; + +public: + //------------------------------------------------------------------ + /// Default constructor makes an empty list. + //------------------------------------------------------------------ + BreakpointSiteList(); + + //------------------------------------------------------------------ + /// Destructor, currently does nothing. + //------------------------------------------------------------------ + ~BreakpointSiteList(); + + //------------------------------------------------------------------ + /// Add a BreakpointSite to the list. + /// + /// @param[in] bp_site_sp + /// A shared pointer to a breakpoint site being added to the list. + /// + /// @return + /// The ID of the BreakpointSite in the list. + //------------------------------------------------------------------ + lldb::break_id_t + Add (const lldb::BreakpointSiteSP& bp_site_sp); + + //------------------------------------------------------------------ + /// Standard Dump routine, doesn't do anything at present. + /// @param[in] s + /// Stream into which to dump the description. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site at address + /// \a addr. + /// + /// @param[in] addr + /// The address to look for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL + /// pointer if no breakpoint site exists with a matching address. + //------------------------------------------------------------------ + lldb::BreakpointSiteSP + FindByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site with id \a breakID. + /// + /// @param[in] breakID + /// The breakpoint site ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + lldb::BreakpointSiteSP + FindByID (lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the breakpoint site with id \a breakID - const version. + /// + /// @param[in] breakID + /// The breakpoint site ID to seek for. + /// + /// @result + /// A shared pointer to the breakpoint site. May contain a NULL pointer if the + /// breakpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::BreakpointSiteSP + FindByID (lldb::break_id_t breakID) const; + + //------------------------------------------------------------------ + /// Returns the breakpoint site id to the breakpoint site at address \a addr. + /// + /// @param[in] addr + /// The address to match. + /// + /// @result + /// The ID of the breakpoint site, or LLDB_INVALID_BREAK_ID. + //------------------------------------------------------------------ + lldb::break_id_t + FindIDByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns whether the breakpoint site \a bp_site_id has \a bp_id + // as one of its owners. + /// + /// @param[in] bp_site_id + /// The breakpoint site id to query. + /// + /// @param[in] bp_id + /// The breakpoint id to look for in \a bp_site_id. + /// + /// @result + /// True if \a bp_site_id exists in the site list AND \a bp_id is one of the + /// owners of that site. + //------------------------------------------------------------------ + bool + BreakpointSiteContainsBreakpoint (lldb::break_id_t bp_site_id, lldb::break_id_t bp_id); + + void + ForEach (std::function const &callback); + + //------------------------------------------------------------------ + /// Removes the breakpoint site given by \b breakID from this list. + /// + /// @param[in] breakID + /// The breakpoint site index to remove. + /// + /// @result + /// \b true if the breakpoint site \a breakID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Removes the breakpoint site at address \a addr from this list. + /// + /// @param[in] addr + /// The address from which to remove a breakpoint site. + /// + /// @result + /// \b true if \a addr had a breakpoint site to remove from the list. + //------------------------------------------------------------------ + bool + RemoveByAddress (lldb::addr_t addr); + + bool + FindInRange (lldb::addr_t lower_bound, lldb::addr_t upper_bound, BreakpointSiteList &bp_site_list) const; + + typedef void (*BreakpointSiteSPMapFunc) (lldb::BreakpointSiteSP &bp, void *baton); + + //------------------------------------------------------------------ + /// Enquires of the breakpoint site on in this list with ID \a breakID whether + /// we should stop for the breakpoint or not. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] breakID + /// This break ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context, lldb::break_id_t breakID); + + //------------------------------------------------------------------ + /// Returns the number of elements in the list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_bp_site_list.size(); + } + + bool + IsEmpty() const + { + Mutex::Locker locker(m_mutex); + return m_bp_site_list.empty(); + } +protected: + typedef std::map collection; + + collection::iterator + GetIDIterator(lldb::break_id_t breakID); + + collection::const_iterator + GetIDConstIterator(lldb::break_id_t breakID) const; + + mutable Mutex m_mutex; + collection m_bp_site_list; // The breakpoint site list. +}; + +} // namespace lldb_private + +#endif // liblldb_BreakpointSiteList_h_ diff --git a/include/lldb/Breakpoint/Stoppoint.h b/include/lldb/Breakpoint/Stoppoint.h new file mode 100644 index 00000000000..c294830f15e --- /dev/null +++ b/include/lldb/Breakpoint/Stoppoint.h @@ -0,0 +1,63 @@ +//===-- Stoppoint.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Stoppoint_h_ +#define liblldb_Stoppoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { + +class Stoppoint +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Stoppoint(); + + virtual + ~Stoppoint(); + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + virtual void + Dump (Stream *) = 0; + + virtual bool + IsEnabled () = 0; + + virtual void + SetEnabled (bool enable) = 0; + + lldb::break_id_t + GetID () const; + + void + SetID (lldb::break_id_t bid); + +protected: + lldb::break_id_t m_bid; + +private: + //------------------------------------------------------------------ + // For Stoppoint only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Stoppoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Stoppoint_h_ diff --git a/include/lldb/Breakpoint/StoppointCallbackContext.h b/include/lldb/Breakpoint/StoppointCallbackContext.h new file mode 100644 index 00000000000..78327e29134 --- /dev/null +++ b/include/lldb/Breakpoint/StoppointCallbackContext.h @@ -0,0 +1,58 @@ +//===-- StoppointCallbackContext.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StoppointCallbackContext_h_ +#define liblldb_StoppointCallbackContext_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class StoppointCallbackContext StoppointCallbackContext.h "lldb/Breakpoint/StoppointCallbackContext.h" +/// @brief Class holds the information that a breakpoint callback needs to evaluate this stop. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// When we hit a breakpoint we need to package up whatever information is needed +/// to evaluate breakpoint commands and conditions. This class is the container of +/// that information. +//---------------------------------------------------------------------- + +class StoppointCallbackContext +{ +public: + StoppointCallbackContext(); + + StoppointCallbackContext(Event *event, const ExecutionContext &exe_ctx, bool synchronously = false); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the event, process and thread to NULL, and the frame index to an + /// invalid value. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Event *event; // This is the event, the callback can modify this to indicate + // the meaning of the breakpoint hit + ExecutionContextRef exe_ctx_ref; // This tells us where we have stopped, what thread. + bool is_synchronous; // Is the callback being executed synchronously with the breakpoint, + // or asynchronously as the event is retrieved? +}; + +} // namespace lldb_private + +#endif // liblldb_StoppointCallbackContext_h_ diff --git a/include/lldb/Breakpoint/StoppointLocation.h b/include/lldb/Breakpoint/StoppointLocation.h new file mode 100644 index 00000000000..ccedc511951 --- /dev/null +++ b/include/lldb/Breakpoint/StoppointLocation.h @@ -0,0 +1,147 @@ +//===-- StoppointLocation.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StoppointLocation_h_ +#define liblldb_StoppointLocation_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +// #include "lldb/Breakpoint/BreakpointOptions.h" + +namespace lldb_private { + +class StoppointLocation +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StoppointLocation (lldb::break_id_t bid, + lldb::addr_t m_addr, + bool hardware); + + StoppointLocation (lldb::break_id_t bid, + lldb::addr_t m_addr, + uint32_t byte_size, + bool hardware); + + virtual + ~StoppointLocation (); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + // Methods + //------------------------------------------------------------------ + virtual lldb::addr_t + GetLoadAddress() const + { + return m_addr; + } + + virtual void + SetLoadAddress (lldb::addr_t addr) + { + m_addr = addr; + } + + uint32_t + GetByteSize () const + { + return m_byte_size; + } + + uint32_t + GetHitCount () const + { + return m_hit_count; + } + + uint32_t + GetHardwareIndex () const + { + return m_hw_index; + } + + + bool + HardwarePreferred () const + { + return m_hw_preferred; + } + + virtual bool + IsHardware () const + { + return m_hw_index != LLDB_INVALID_INDEX32; + } + + + virtual bool + ShouldStop (StoppointCallbackContext *context) + { + return true; + } + + virtual void + Dump (Stream *stream) const + { + } + + void + SetHardwareIndex (uint32_t index) + { + m_hw_index = index; + } + + + lldb::break_id_t + GetID () const + { + return m_loc_id; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StoppointLocation can see and modify these + //------------------------------------------------------------------ + lldb::break_id_t m_loc_id; // Stoppoint location ID + lldb::addr_t m_addr; // The load address of this stop point. The base Stoppoint doesn't + // store a full Address since that's not needed for the breakpoint sites. + bool m_hw_preferred; // 1 if this point has been requested to be set using hardware (which may fail due to lack of resources) + uint32_t m_hw_index; // The hardware resource index for this breakpoint/watchpoint + uint32_t m_byte_size; // The size in bytes of stop location. e.g. the length of the trap opcode for + // software breakpoints, or the optional length in bytes for + // hardware breakpoints, or the length of the watchpoint. + uint32_t m_hit_count; // Number of times this breakpoint/watchpoint has been hit + + // If you override this, be sure to call the base class to increment the internal counter. + void + IncrementHitCount () + { + ++m_hit_count; + } + +private: + //------------------------------------------------------------------ + // For StoppointLocation only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN(StoppointLocation); + StoppointLocation(); // Disallow default constructor +}; + +} // namespace lldb_private + +#endif // liblldb_StoppointLocation_h_ diff --git a/include/lldb/Breakpoint/Watchpoint.h b/include/lldb/Breakpoint/Watchpoint.h new file mode 100644 index 00000000000..5dbed03d540 --- /dev/null +++ b/include/lldb/Breakpoint/Watchpoint.h @@ -0,0 +1,252 @@ +//===-- Watchpoint.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Watchpoint_h_ +#define liblldb_Watchpoint_h_ + +// C Includes + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Target.h" +#include "lldb/Core/UserID.h" +#include "lldb/Breakpoint/WatchpointOptions.h" +#include "lldb/Breakpoint/StoppointLocation.h" + +namespace lldb_private { + +class Watchpoint : + public std::enable_shared_from_this, + public StoppointLocation +{ +public: + + class WatchpointEventData : + public EventData + { + public: + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + WatchpointEventData (lldb::WatchpointEventType sub_type, + const lldb::WatchpointSP &new_watchpoint_sp); + + virtual + ~WatchpointEventData(); + + lldb::WatchpointEventType + GetWatchpointEventType () const; + + lldb::WatchpointSP & + GetWatchpoint (); + + virtual void + Dump (Stream *s) const; + + static lldb::WatchpointEventType + GetWatchpointEventTypeFromEvent (const lldb::EventSP &event_sp); + + static lldb::WatchpointSP + GetWatchpointFromEvent (const lldb::EventSP &event_sp); + + static const WatchpointEventData * + GetEventDataFromEvent (const Event *event_sp); + + private: + + lldb::WatchpointEventType m_watchpoint_event; + lldb::WatchpointSP m_new_watchpoint_sp; + + DISALLOW_COPY_AND_ASSIGN (WatchpointEventData); + }; + + Watchpoint (Target& target, lldb::addr_t addr, uint32_t size, const ClangASTType *type, bool hardware = true); + ~Watchpoint (); + + void + IncrementFalseAlarmsAndReviseHitCount(); + + bool + IsEnabled () const; + + void + SetEnabled (bool enabled, bool notify = true); + + virtual bool + IsHardware () const; + + virtual bool + ShouldStop (StoppointCallbackContext *context); + + bool WatchpointRead () const; + bool WatchpointWrite () const; + uint32_t GetIgnoreCount () const; + void SetIgnoreCount (uint32_t n); + void SetWatchpointType (uint32_t type, bool notify = true); + void SetDeclInfo (const std::string &str); + std::string GetWatchSpec(); + void SetWatchSpec (const std::string &str); + + // Snapshot management interface. + bool IsWatchVariable() const; + void SetWatchVariable(bool val); + bool CaptureWatchedValue (const ExecutionContext &exe_ctx); + + void GetDescription (Stream *s, lldb::DescriptionLevel level); + void Dump (Stream *s) const; + void DumpSnapshots (Stream *s, const char * prefix = NULL) const; + void DumpWithLevel (Stream *s, lldb::DescriptionLevel description_level) const; + Target &GetTarget() { return m_target; } + const Error &GetError() { return m_error; } + + //------------------------------------------------------------------ + /// Returns the WatchpointOptions structure set for this watchpoint. + /// + /// @return + /// A pointer to this watchpoint's WatchpointOptions. + //------------------------------------------------------------------ + WatchpointOptions * + GetOptions () { return &m_options; } + + //------------------------------------------------------------------ + /// Set the callback action invoked when the watchpoint is hit. + /// + /// @param[in] callback + /// The method that will get called when the watchpoint is hit. + /// @param[in] callback_baton + /// A void * pointer that will get passed back to the callback function. + /// @param[in] is_synchronous + /// If \b true the callback will be run on the private event thread + /// before the stop event gets reported. If false, the callback will get + /// handled on the public event thead after the stop has been posted. + /// + /// @return + /// \b true if the process should stop when you hit the watchpoint. + /// \b false if it should continue. + //------------------------------------------------------------------ + void + SetCallback (WatchpointHitCallback callback, + void *callback_baton, + bool is_synchronous = false); + + void + SetCallback (WatchpointHitCallback callback, + const lldb::BatonSP &callback_baton_sp, + bool is_synchronous = false); + + void ClearCallback(); + + //------------------------------------------------------------------ + /// Invoke the callback action when the watchpoint is hit. + /// + /// @param[in] context + /// Described the watchpoint event. + /// + /// @return + /// \b true if the target should stop at this watchpoint and \b false not. + //------------------------------------------------------------------ + bool + InvokeCallback (StoppointCallbackContext *context); + + //------------------------------------------------------------------ + // Condition + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Set the watchpoint's condition. + /// + /// @param[in] condition + /// The condition expression to evaluate when the watchpoint is hit. + /// Pass in NULL to clear the condition. + //------------------------------------------------------------------ + void SetCondition (const char *condition); + + //------------------------------------------------------------------ + /// Return a pointer to the text of the condition expression. + /// + /// @return + /// A pointer to the condition expression text, or NULL if no + // condition has been set. + //------------------------------------------------------------------ + const char *GetConditionText () const; + + void + TurnOnEphemeralMode(); + + void + TurnOffEphemeralMode(); + + bool + IsDisabledDuringEphemeralMode(); + + const ClangASTType & + GetClangASTType() + { + return m_type; + } + + +private: + friend class Target; + friend class WatchpointList; + + void ResetHitCount() { m_hit_count = 0; } + + Target &m_target; + bool m_enabled; // Is this watchpoint enabled + bool m_is_hardware; // Is this a hardware watchpoint + bool m_is_watch_variable; // True if set via 'watchpoint set variable'. + bool m_is_ephemeral; // True if the watchpoint is in the ephemeral mode, meaning that it is + // undergoing a pair of temporary disable/enable actions to avoid recursively + // triggering further watchpoint events. + uint32_t m_disabled_count; // Keep track of the count that the watchpoint is disabled while in ephemeral mode. + // At the end of the ephemeral mode when the watchpoint is to be enabled agian, + // we check the count, if it is more than 1, it means the user-supplied actions + // actually want the watchpoint to be disabled! + uint32_t m_watch_read:1, // 1 if we stop when the watched data is read from + m_watch_write:1, // 1 if we stop when the watched data is written to + m_watch_was_read:1, // Set to 1 when watchpoint is hit for a read access + m_watch_was_written:1; // Set to 1 when watchpoint is hit for a write access + uint32_t m_ignore_count; // Number of times to ignore this watchpoint + uint32_t m_false_alarms; // Number of false alarms. + std::string m_decl_str; // Declaration information, if any. + std::string m_watch_spec_str; // Spec for the watchpoint. + lldb::ValueObjectSP m_old_value_sp; + lldb::ValueObjectSP m_new_value_sp; + ClangASTType m_type; + Error m_error; // An error object describing errors associated with this watchpoint. + WatchpointOptions m_options; // Settable watchpoint options, which is a delegate to handle + // the callback machinery. + bool m_being_created; + + std::unique_ptr m_condition_ap; // The condition to test. + + void SetID(lldb::watch_id_t id) { m_loc_id = id; } + + void + SendWatchpointChangedEvent (lldb::WatchpointEventType eventKind); + + void + SendWatchpointChangedEvent (WatchpointEventData *data); + + DISALLOW_COPY_AND_ASSIGN (Watchpoint); +}; + +} // namespace lldb_private + +#endif // liblldb_Watchpoint_h_ diff --git a/include/lldb/Breakpoint/WatchpointList.h b/include/lldb/Breakpoint/WatchpointList.h new file mode 100644 index 00000000000..d16cb25e3b7 --- /dev/null +++ b/include/lldb/Breakpoint/WatchpointList.h @@ -0,0 +1,276 @@ +//===-- WatchpointList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WatchpointList_h_ +#define liblldb_WatchpointList_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class WatchpointList WatchpointList.h "lldb/Breakpoint/WatchpointList.h" +/// @brief This class is used by Watchpoint to manage a list of watchpoints, +// each watchpoint in the list has a unique ID, and is unique by Address as +// well. +//---------------------------------------------------------------------- + +class WatchpointList +{ +// Only Target can make the watchpoint list, or add elements to it. +// This is not just some random collection of watchpoints. Rather, the act of +// adding the watchpoint to this list sets its ID. +friend class Watchpoint; +friend class Target; + +public: + //------------------------------------------------------------------ + /// Default constructor makes an empty list. + //------------------------------------------------------------------ + WatchpointList(); + + //------------------------------------------------------------------ + /// Destructor, currently does nothing. + //------------------------------------------------------------------ + ~WatchpointList(); + + //------------------------------------------------------------------ + /// Add a Watchpoint to the list. + /// + /// @param[in] wp_sp + /// A shared pointer to a watchpoint being added to the list. + /// + /// @return + /// The ID of the Watchpoint in the list. + //------------------------------------------------------------------ + lldb::watch_id_t + Add (const lldb::WatchpointSP& wp_sp, bool notify); + + //------------------------------------------------------------------ + /// Standard "Dump" method. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Dump with lldb::DescriptionLevel. + //------------------------------------------------------------------ + void + DumpWithLevel (Stream *s, lldb::DescriptionLevel description_level) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint at address + /// \a addr - + /// const version. + /// + /// @param[in] addr + /// The address to look for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL + /// pointer if the watchpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::WatchpointSP + FindByAddress (lldb::addr_t addr) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with watchpoint spec + /// \a spec - + /// const version. + /// + /// @param[in] spec + /// The watchpoint spec to look for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL + /// pointer if the watchpoint doesn't exist. + //------------------------------------------------------------------ + const lldb::WatchpointSP + FindBySpec (std::string spec) const; + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with id + /// \a watchID, const + /// version. + /// + /// @param[in] watchID + /// The watchpoint location ID to seek for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL + /// pointer if the watchpoint doesn't exist. + //------------------------------------------------------------------ + lldb::WatchpointSP + FindByID (lldb::watch_id_t watchID) const; + + //------------------------------------------------------------------ + /// Returns the watchpoint id to the watchpoint + /// at address \a addr. + /// + /// @param[in] addr + /// The address to match. + /// + /// @result + /// The ID of the watchpoint, or LLDB_INVALID_WATCH_ID. + //------------------------------------------------------------------ + lldb::watch_id_t + FindIDByAddress (lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Returns the watchpoint id to the watchpoint + /// with watchpoint spec \a spec. + /// + /// @param[in] spec + /// The watchpoint spec to match. + /// + /// @result + /// The ID of the watchpoint, or LLDB_INVALID_WATCH_ID. + //------------------------------------------------------------------ + lldb::watch_id_t + FindIDBySpec (std::string spec); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with index \a i. + /// + /// @param[in] i + /// The watchpoint index to seek for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL pointer if + /// the watchpoint doesn't exist. + //------------------------------------------------------------------ + lldb::WatchpointSP + GetByIndex (uint32_t i); + + //------------------------------------------------------------------ + /// Returns a shared pointer to the watchpoint with index \a i, const + /// version. + /// + /// @param[in] i + /// The watchpoint index to seek for. + /// + /// @result + /// A shared pointer to the watchpoint. May contain a NULL pointer if + /// the watchpoint location doesn't exist. + //------------------------------------------------------------------ + const lldb::WatchpointSP + GetByIndex (uint32_t i) const; + + //------------------------------------------------------------------ + /// Removes the watchpoint given by \b watchID from this list. + /// + /// @param[in] watchID + /// The watchpoint ID to remove. + /// + /// @result + /// \b true if the watchpoint \a watchID was in the list. + //------------------------------------------------------------------ + bool + Remove (lldb::watch_id_t watchID, bool notify); + + //------------------------------------------------------------------ + /// Returns the number hit count of all watchpoints in this list. + /// + /// @result + /// Hit count of all watchpoints in this list. + //------------------------------------------------------------------ + uint32_t + GetHitCount () const; + + //------------------------------------------------------------------ + /// Enquires of the watchpoint in this list with ID \a watchID whether we + /// should stop. + /// + /// @param[in] context + /// This contains the information about this stop. + /// + /// @param[in] watchID + /// This watch ID that we hit. + /// + /// @return + /// \b true if we should stop, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldStop (StoppointCallbackContext *context, + lldb::watch_id_t watchID); + + //------------------------------------------------------------------ + /// Returns the number of elements in this watchpoint list. + /// + /// @result + /// The number of elements. + //------------------------------------------------------------------ + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_watchpoints.size(); + } + + //------------------------------------------------------------------ + /// Print a description of the watchpoints in this list to the stream \a s. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The description level that indicates the detail level to + /// provide. + /// + /// @see lldb::DescriptionLevel + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + void + SetEnabledAll (bool enabled); + + void + RemoveAll (bool notify); + + //------------------------------------------------------------------ + /// Sets the passed in Locker to hold the Watchpoint List mutex. + /// + /// @param[in] locker + /// The locker object that is set. + //------------------------------------------------------------------ + void + GetListMutex (lldb_private::Mutex::Locker &locker); + +protected: + typedef std::list wp_collection; + typedef std::vector id_vector; + + id_vector + GetWatchpointIDs() const; + + wp_collection::iterator + GetIDIterator(lldb::watch_id_t watchID); + + wp_collection::const_iterator + GetIDConstIterator(lldb::watch_id_t watchID) const; + + wp_collection m_watchpoints; + mutable Mutex m_mutex; + + lldb::watch_id_t m_next_wp_id; +}; + +} // namespace lldb_private + +#endif // liblldb_WatchpointList_h_ diff --git a/include/lldb/Breakpoint/WatchpointOptions.h b/include/lldb/Breakpoint/WatchpointOptions.h new file mode 100644 index 00000000000..64c65f92b44 --- /dev/null +++ b/include/lldb/Breakpoint/WatchpointOptions.h @@ -0,0 +1,255 @@ +//===-- WatchpointOptions.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_WatchpointOptions_h_ +#define liblldb_WatchpointOptions_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Baton.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class WatchpointOptions WatchpointOptions.h "lldb/Breakpoint/WatchpointOptions.h" +/// @brief Class that manages the options on a watchpoint. +//---------------------------------------------------------------------- + +class WatchpointOptions +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + //------------------------------------------------------------------ + /// Default constructor. The watchpoint is enabled, and has no condition, + /// callback, ignore count, etc... + //------------------------------------------------------------------ + WatchpointOptions(); + WatchpointOptions(const WatchpointOptions& rhs); + + static WatchpointOptions * + CopyOptionsNoCallback (WatchpointOptions &rhs); + //------------------------------------------------------------------ + /// This constructor allows you to specify all the watchpoint options. + /// + /// @param[in] callback + /// This is the plugin for some code that gets run, returns \b true if we are to stop. + /// + /// @param[in] baton + /// Client data that will get passed to the callback. + /// + /// @param[in] thread_id + /// Only stop if \a thread_id hits the watchpoint. + //------------------------------------------------------------------ + WatchpointOptions(WatchpointHitCallback callback, + void *baton, + lldb::tid_t thread_id = LLDB_INVALID_THREAD_ID); + + virtual ~WatchpointOptions(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const WatchpointOptions& + operator=(const WatchpointOptions& rhs); + + //------------------------------------------------------------------ + // Callbacks + // + // Watchpoint callbacks come in two forms, synchronous and asynchronous. Synchronous callbacks will get + // run before any of the thread plans are consulted, and if they return false the target will continue + // "under the radar" of the thread plans. There are a couple of restrictions to synchronous callbacks: + // 1) They should NOT resume the target themselves. Just return false if you want the target to restart. + // 2) Watchpoints with synchronous callbacks can't have conditions (or rather, they can have them, but they + // won't do anything. Ditto with ignore counts, etc... You are supposed to control that all through the + // callback. + // Asynchronous callbacks get run as part of the "ShouldStop" logic in the thread plan. The logic there is: + // a) If the watchpoint is thread specific and not for this thread, continue w/o running the callback. + // b) If the ignore count says we shouldn't stop, then ditto. + // c) If the condition says we shouldn't stop, then ditto. + // d) Otherwise, the callback will get run, and if it returns true we will stop, and if false we won't. + // The asynchronous callback can run the target itself, but at present that should be the last action the + // callback does. We will relax this condition at some point, but it will take a bit of plumbing to get + // that to work. + // + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Adds a callback to the watchpoint option set. + /// + /// @param[in] callback + /// The function to be called when the watchpoint gets hit. + /// + /// @param[in] baton_sp + /// A baton which will get passed back to the callback when it is invoked. + /// + /// @param[in] synchronous + /// Whether this is a synchronous or asynchronous callback. See discussion above. + //------------------------------------------------------------------ + void SetCallback (WatchpointHitCallback callback, const lldb::BatonSP &baton_sp, bool synchronous = false); + + + //------------------------------------------------------------------ + /// Remove the callback from this option set. + //------------------------------------------------------------------ + void ClearCallback (); + + // The rest of these functions are meant to be used only within the watchpoint handling mechanism. + + //------------------------------------------------------------------ + /// Use this function to invoke the callback for a specific stop. + /// + /// @param[in] context + /// The context in which the callback is to be invoked. This includes the stop event, the + /// execution context of the stop (since you might hit the same watchpoint on multiple threads) and + /// whether we are currently executing synchronous or asynchronous callbacks. + /// + /// @param[in] watch_id + /// The watchpoint ID that owns this option set. + /// + /// @return + /// The callback return value. + //------------------------------------------------------------------ + bool InvokeCallback (StoppointCallbackContext *context, lldb::user_id_t watch_id); + + //------------------------------------------------------------------ + /// Used in InvokeCallback to tell whether it is the right time to run this kind of callback. + /// + /// @return + /// The synchronicity of our callback. + //------------------------------------------------------------------ + bool IsCallbackSynchronous () { + return m_callback_is_synchronous; + } + + //------------------------------------------------------------------ + /// Fetch the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + Baton *GetBaton (); + + //------------------------------------------------------------------ + /// Fetch a const version of the baton from the callback. + /// + /// @return + /// The baton. + //------------------------------------------------------------------ + const Baton *GetBaton () const; + + //------------------------------------------------------------------ + /// Return the current thread spec for this option. This will return NULL if the no thread + /// specifications have been set for this Option yet. + /// @return + /// The thread specification pointer for this option, or NULL if none has + /// been set yet. + //------------------------------------------------------------------ + const ThreadSpec * + GetThreadSpecNoCreate () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the ThreadSpec for this option, creating it. + /// if it hasn't been created already. This API is used for setting the + /// ThreadSpec items for this option. + //------------------------------------------------------------------ + ThreadSpec * + GetThreadSpec (); + + void + SetThreadID(lldb::tid_t thread_id); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Get description for callback only. + //------------------------------------------------------------------ + void + GetCallbackDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Returns true if the watchpoint option has a callback set. + //------------------------------------------------------------------ + bool + HasCallback(); + + //------------------------------------------------------------------ + /// This is the default empty callback. + /// @return + /// The thread id for which the watchpoint hit will stop, + /// LLDB_INVALID_THREAD_ID for all threads. + //------------------------------------------------------------------ + static bool + NullCallback (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t watch_id); + + + struct CommandData + { + CommandData () : + user_source(), + script_source(), + stop_on_error(true) + { + } + + ~CommandData () + { + } + + StringList user_source; + std::string script_source; + bool stop_on_error; + }; + + class CommandBaton : public Baton + { + public: + CommandBaton (CommandData *data) : + Baton (data) + { + } + + virtual + ~CommandBaton () + { + delete ((CommandData *)m_data); + m_data = NULL; + } + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + }; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from WatchpointOptions can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For WatchpointOptions only + //------------------------------------------------------------------ + WatchpointHitCallback m_callback; // This is the callback function pointer + lldb::BatonSP m_callback_baton_sp; // This is the client data for the callback + bool m_callback_is_synchronous; + std::unique_ptr m_thread_spec_ap; // Thread for which this watchpoint will take +}; + +} // namespace lldb_private + +#endif // liblldb_WatchpointOptions_h_ diff --git a/include/lldb/Core/Address.h b/include/lldb/Core/Address.h new file mode 100644 index 00000000000..60cd4a86bd4 --- /dev/null +++ b/include/lldb/Core/Address.h @@ -0,0 +1,570 @@ +//===-- Address.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Address_h_ +#define liblldb_Address_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolContextScope.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Address Address.h "lldb/Core/Address.h" +/// @brief A section + offset based address class. +/// +/// The Address class allows addresses to be relative to a section +/// that can move during runtime due to images (executables, shared +/// libraries, bundles, frameworks) being loaded at different +/// addresses than the addresses found in the object file that +/// represents them on disk. There are currently two types of addresses +/// for a section: +/// @li file addresses +/// @li load addresses +/// +/// File addresses represent the virtual addresses that are in the "on +/// disk" object files. These virtual addresses are converted to be +/// relative to unique sections scoped to the object file so that +/// when/if the addresses slide when the images are loaded/unloaded +/// in memory, we can easily track these changes without having to +/// update every object (compile unit ranges, line tables, function +/// address ranges, lexical block and inlined subroutine address +/// ranges, global and static variables) each time an image is loaded or +/// unloaded. +/// +/// Load addresses represent the virtual addresses where each section +/// ends up getting loaded at runtime. Before executing a program, it +/// is common for all of the load addresses to be unresolved. When a +/// DynamicLoader plug-in receives notification that shared libraries +/// have been loaded/unloaded, the load addresses of the main executable +/// and any images (shared libraries) will be resolved/unresolved. When +/// this happens, breakpoints that are in one of these sections can be +/// set/cleared. +//---------------------------------------------------------------------- +class Address +{ +public: + //------------------------------------------------------------------ + /// Dump styles allow the Address::Dump(Stream *,DumpStyle) const + /// function to display Address contents in a variety of ways. + //------------------------------------------------------------------ + typedef enum { + DumpStyleInvalid, ///< Invalid dump style + DumpStyleSectionNameOffset, ///< Display as the section name + offset. + ///< \code + /// // address for printf in libSystem.B.dylib as a section name + offset + /// libSystem.B.dylib.__TEXT.__text + 0x0005cfdf + /// \endcode + DumpStyleSectionPointerOffset, ///< Display as the section pointer + offset (debug output). + ///< \code + /// // address for printf in libSystem.B.dylib as a section pointer + offset + /// (lldb::Section *)0x35cc50 + 0x000000000005cfdf \endcode + DumpStyleFileAddress, ///< Display as the file address (if any). + ///< \code + /// // address for printf in libSystem.B.dylib as a file address + /// 0x000000000005dcff \endcode + DumpStyleModuleWithFileAddress, ///< Display as the file address with the module name prepended (if any). + ///< \code + /// // address for printf in libSystem.B.dylib as a file address + /// libSystem.B.dylib[0x000000000005dcff] \endcode + DumpStyleLoadAddress, ///< Display as the load address (if resolved). + ///< \code + /// // address for printf in libSystem.B.dylib as a load address + /// 0x00007fff8306bcff \endcode + DumpStyleResolvedDescription, ///< Display the details about what an address resolves to. This can + ///< be anything from a symbol context summary (module, function/symbol, + ///< and file and line), to information about what the pointer points to + ///< if the address is in a section (section of pointers, c strings, etc). + DumpStyleResolvedDescriptionNoModule, + DumpStyleDetailedSymbolContext, ///< Detailed symbol context information for an address for all symbol + ///< context members. + DumpStyleResolvedPointerDescription ///< Dereference a pointer at the current address and then lookup the + ///< dereferenced address using DumpStyleResolvedDescription + } DumpStyle; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with a invalid section (NULL) and an invalid + /// offset (LLDB_INVALID_ADDRESS). + //------------------------------------------------------------------ + Address () : + m_section_wp (), + m_offset (LLDB_INVALID_ADDRESS) + { + } + + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the another Address object \a rhs. + /// + /// @param[in] rhs + /// A const Address object reference to copy. + //------------------------------------------------------------------ + Address (const Address& rhs) : + m_section_wp (rhs.m_section_wp), + m_offset(rhs.m_offset.load()) + { + } + + //------------------------------------------------------------------ + /// Construct with a section pointer and offset. + /// + /// Initialize the address with the supplied \a section and \a + /// offset. + /// + /// @param[in] section + /// A section pointer to a valid lldb::Section, or NULL if the + /// address doesn't have a section or will get resolved later. + /// + /// @param[in] offset + /// The offset in bytes into \a section. + //------------------------------------------------------------------ + Address (const lldb::SectionSP §ion_sp, lldb::addr_t offset) : + m_section_wp (), // Don't init with section_sp in case section_sp is invalid (the weak_ptr will throw) + m_offset (offset) + { + if (section_sp) + m_section_wp = section_sp; + } + + //------------------------------------------------------------------ + /// Construct with a virtual address and section list. + /// + /// Initialize and resolve the address with the supplied virtual + /// address \a file_addr. + /// + /// @param[in] file_addr + /// A virtual file address. + /// + /// @param[in] section_list + /// A list of sections, one of which may contain the \a file_addr. + //------------------------------------------------------------------ + Address (lldb::addr_t file_addr, const SectionList * section_list); + + Address (lldb::addr_t abs_addr); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the address value from another Address object \a rhs + /// into \a this object. + /// + /// @param[in] rhs + /// A const Address object reference to copy. + /// + /// @return + /// A const Address object reference to \a this. + //------------------------------------------------------------------ +#ifndef SWIG + const Address& + operator= (const Address& rhs); +#endif + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the section to an invalid value (NULL) and an invalid + /// offset (LLDB_INVALID_ADDRESS). + //------------------------------------------------------------------ + void + Clear () + { + m_section_wp.reset(); + m_offset = LLDB_INVALID_ADDRESS; + } + + //------------------------------------------------------------------ + /// Compare two Address objects. + /// + /// @param[in] lhs + /// The Left Hand Side const Address object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const Address object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + CompareFileAddress (const Address& lhs, const Address& rhs); + + static int + CompareLoadAddress (const Address& lhs, const Address& rhs, Target *target); + + static int + CompareModulePointerAndOffset (const Address& lhs, const Address& rhs); + + // For use with std::map, std::multi_map + class ModulePointerAndOffsetLessThanFunctionObject + { + public: + ModulePointerAndOffsetLessThanFunctionObject () {} + + bool + operator() (const Address& a, const Address& b) const + { + return Address::CompareModulePointerAndOffset(a, b) < 0; + } + }; + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. There are many ways to display a section + /// offset based address, and \a style lets the user choose. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] style + /// The display style for the address. + /// + /// @param[in] fallback_style + /// The display style for the address. + /// + /// @return + /// Returns \b true if the address was able to be displayed. + /// File and load addresses may be unresolved and it may not be + /// possible to display a valid value, \b false will be returned + /// in such cases. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + bool + Dump (Stream *s, + ExecutionContextScope *exe_scope, + DumpStyle style, + DumpStyle fallback_style = DumpStyleInvalid, + uint32_t addr_byte_size = UINT32_MAX) const; + + lldb::AddressClass + GetAddressClass () const; + + //------------------------------------------------------------------ + /// Get the file address. + /// + /// If an address comes from a file on disk that has section + /// relative addresses, then it has a virtual address that is + /// relative to unique section in the object file. + /// + /// @return + /// The valid file virtual address, or LLDB_INVALID_ADDRESS if + /// the address doesn't have a file virtual address (image is + /// from memory only with no representation on disk). + //------------------------------------------------------------------ + lldb::addr_t + GetFileAddress () const; + + //------------------------------------------------------------------ + /// Get the load address. + /// + /// If an address comes from a file on disk that has section + /// relative addresses, then it has a virtual address that is + /// relative to unique section in the object file. Sections get + /// resolved at runtime by DynamicLoader plug-ins as images + /// (executables and shared libraries) get loaded/unloaded. If a + /// section is loaded, then the load address can be resolved. + /// + /// @return + /// The valid load virtual address, or LLDB_INVALID_ADDRESS if + /// the address is currently not loaded. + //------------------------------------------------------------------ + lldb::addr_t + GetLoadAddress (Target *target) const; + + //------------------------------------------------------------------ + /// Get the load address as a callable code load address. + /// + /// This function will first resolve its address to a load address. + /// Then, if the address turns out to be in code address, return the + /// load address that would be required to call or return to. The + /// address might have extra bits set (bit zero will be set to Thumb + /// functions for an ARM target) that are required when changing the + /// program counter to setting a return address. + /// + /// @return + /// The valid load virtual address, or LLDB_INVALID_ADDRESS if + /// the address is currently not loaded. + //------------------------------------------------------------------ + lldb::addr_t + GetCallableLoadAddress (Target *target, bool is_indirect = false) const; + + //------------------------------------------------------------------ + /// Get the load address as an opcode load address. + /// + /// This function will first resolve its address to a load address. + /// Then, if the address turns out to be in code address, return the + /// load address for a an opcode. This address object might have + /// extra bits set (bit zero will be set to Thumb functions for an + /// ARM target) that are required for changing the program counter + /// and this function will remove any bits that are intended for + /// these special purposes. The result of this function can be used + /// to safely write a software breakpoint trap to memory. + /// + /// @return + /// The valid load virtual address with extra callable bits + /// removed, or LLDB_INVALID_ADDRESS if the address is currently + /// not loaded. + //------------------------------------------------------------------ + lldb::addr_t + GetOpcodeLoadAddress (Target *target) const; + + //------------------------------------------------------------------ + /// Get the section relative offset value. + /// + /// @return + /// The current offset, or LLDB_INVALID_ADDRESS if this address + /// doesn't contain a valid offset. + //------------------------------------------------------------------ + lldb::addr_t + GetOffset () const { return m_offset; } + + //------------------------------------------------------------------ + /// Check if an address is section offset. + /// + /// When converting a virtual file or load address into a section + /// offset based address, we often need to know if, given a section + /// list, if the address was able to be converted to section offset. + /// This function returns true if the current value contained in + /// this object is section offset based. + /// + /// @return + /// Returns \b true if the address has a valid section and + /// offset, \b false otherwise. + //------------------------------------------------------------------ + bool + IsSectionOffset() const + { + return IsValid() && (GetSection().get() != NULL); + } + + //------------------------------------------------------------------ + /// Check if the object state is valid. + /// + /// A valid Address object contains either a section pointer and + /// and offset (for section offset based addresses), or just a valid + /// offset (for absolute addresses that have no section). + /// + /// @return + /// Returns \b true if the the offset is valid, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid() const + { + return m_offset != LLDB_INVALID_ADDRESS; + } + + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Resolve a file virtual address using a section list. + /// + /// Given a list of sections, attempt to resolve \a addr as a + /// an offset into one of the file sections. + /// + /// @return + /// Returns \b true if \a addr was able to be resolved, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + ResolveAddressUsingFileSections (lldb::addr_t addr, const SectionList *sections); + + //------------------------------------------------------------------ + /// Set the address to represent \a load_addr. + /// + /// The address will attempt to find a loaded section within + /// \a target that contains \a load_addr. If successful, this + /// address object will have a valid section and offset. Else this + /// address object will have no section (NULL) and the offset will + /// be \a load_addr. + /// + /// @param[in] load_addr + /// A load address from a current process. + /// + /// @param[in] target + /// The target to use when trying resolve the address into + /// a section + offset. The Target's SectionLoadList object + /// is used to resolve the address. + /// + /// @return + /// Returns \b true if the load address was resolved to be + /// section/offset, \b false otherwise. It is often ok for an + /// address no not resolve to a section in a module, this often + /// happens for JIT'ed code, or any load addresses on the stack + /// or heap. + //------------------------------------------------------------------ + bool + SetLoadAddress (lldb::addr_t load_addr, Target *target); + + bool + SetOpcodeLoadAddress (lldb::addr_t load_addr, Target *target); + + bool + SetCallableLoadAddress (lldb::addr_t load_addr, Target *target); + + //------------------------------------------------------------------ + /// Get accessor for the module for this address. + /// + /// @return + /// Returns the Module pointer that this address is an offset + /// in, or NULL if this address doesn't belong in a module, or + /// isn't resolved yet. + //------------------------------------------------------------------ + lldb::ModuleSP + GetModule () const; + + //------------------------------------------------------------------ + /// Get const accessor for the section. + /// + /// @return + /// Returns the const lldb::Section pointer that this address is an + /// offset in, or NULL if this address is absolute. + //------------------------------------------------------------------ + lldb::SectionSP + GetSection () const { return m_section_wp.lock(); } + + //------------------------------------------------------------------ + /// Set accessor for the offset. + /// + /// @param[in] offset + /// A new offset value for this object. + /// + /// @return + /// Returns \b true if the offset changed, \b false otherwise. + //------------------------------------------------------------------ + bool + SetOffset (lldb::addr_t offset) + { + bool changed = m_offset != offset; + m_offset = offset; + return changed; + } + + void + SetRawAddress (lldb::addr_t addr) + { + m_section_wp.reset(); + m_offset = addr; + } + + bool + Slide (int64_t offset) + { + if (m_offset != LLDB_INVALID_ADDRESS) + { + m_offset += offset; + return true; + } + return false; + } + + //------------------------------------------------------------------ + /// Set accessor for the section. + /// + /// @param[in] section + /// A new lldb::Section pointer to use as the section base. Can + /// be NULL for absolute addresses that are not relative to + /// any section. + //------------------------------------------------------------------ + void + SetSection (const lldb::SectionSP §ion_sp) + { + m_section_wp = section_sp; + } + + void + ClearSection () + { + m_section_wp.reset(); + } + //------------------------------------------------------------------ + /// Reconstruct a symbol context from an address. + /// + /// This class doesn't inherit from SymbolContextScope because many + /// address objects have short lifespans. Address objects that are + /// section offset can reconstruct their symbol context by looking + /// up the address in the module found in the section. + /// + /// @see SymbolContextScope::CalculateSymbolContext(SymbolContext*) + //------------------------------------------------------------------ + uint32_t + CalculateSymbolContext (SymbolContext *sc, + uint32_t resolve_scope = lldb::eSymbolContextEverything) const; + + lldb::ModuleSP + CalculateSymbolContextModule () const; + + CompileUnit * + CalculateSymbolContextCompileUnit () const; + + Function * + CalculateSymbolContextFunction () const; + + Block * + CalculateSymbolContextBlock () const; + + Symbol * + CalculateSymbolContextSymbol () const; + + bool + CalculateSymbolContextLineEntry (LineEntry &line_entry) const; + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + lldb::SectionWP m_section_wp; ///< The section for the address, can be NULL. + std::atomic m_offset; ///< Offset into section if \a m_section_wp is valid... +}; + + +//---------------------------------------------------------------------- +// 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 operator< (const Address& lhs, const Address& rhs); +bool operator> (const Address& lhs, const Address& rhs); + + + +bool operator== (const Address& lhs, const Address& rhs); +bool operator!= (const Address& lhs, const Address& rhs); + +} // namespace lldb_private + +#endif // liblldb_Address_h_ diff --git a/include/lldb/Core/AddressRange.h b/include/lldb/Core/AddressRange.h new file mode 100644 index 00000000000..bd3ab2ab5da --- /dev/null +++ b/include/lldb/Core/AddressRange.h @@ -0,0 +1,284 @@ +//===-- AddressRange.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressRange_h_ +#define liblldb_AddressRange_h_ + +#include "lldb/Core/Address.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressRange AddressRange.h "lldb/Core/AddressRange.h" +/// @brief A section + offset based address range class. +//---------------------------------------------------------------------- +class AddressRange +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with a invalid section (NULL), an invalid + /// offset (LLDB_INVALID_ADDRESS), and zero byte size. + //------------------------------------------------------------------ + AddressRange (); + + //------------------------------------------------------------------ + /// Construct with a section pointer, offset, and byte_size. + /// + /// Initialize the address with the supplied \a section, \a + /// offset and \a byte_size. + /// + /// @param[in] section + /// A section pointer to a valid lldb::Section, or NULL if the + /// address doesn't have a section or will get resolved later. + /// + /// @param[in] offset + /// The offset in bytes into \a section. + /// + /// @param[in] byte_size + /// The size in bytes of the address range. + //------------------------------------------------------------------ + AddressRange (const lldb::SectionSP §ion, lldb::addr_t offset, lldb::addr_t byte_size); + + //------------------------------------------------------------------ + /// Construct with a virtual address, section list and byte size. + /// + /// Initialize and resolve the address with the supplied virtual + /// address \a file_addr, and byte size \a byte_size. + /// + /// @param[in] file_addr + /// A virtual address. + /// + /// @param[in] byte_size + /// The size in bytes of the address range. + /// + /// @param[in] section_list + /// A list of sections, one of which may contain the \a vaddr. + //------------------------------------------------------------------ + AddressRange (lldb::addr_t file_addr, lldb::addr_t byte_size, const SectionList *section_list = NULL); + + //------------------------------------------------------------------ + /// Construct with a Address object address and byte size. + /// + /// Initialize by copying the section offset address in \a so_addr, + /// and setting the byte size to \a byte_size. + /// + /// @param[in] so_addr + /// A section offset address object. + /// + /// @param[in] byte_size + /// The size in bytes of the address range. + //------------------------------------------------------------------ + AddressRange (const Address& so_addr, lldb::addr_t byte_size); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + ~AddressRange (); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the section to an invalid value (NULL), an invalid offset + /// (LLDB_INVALID_ADDRESS) and a zero byte size. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Check if a section offset address is contained in this range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if \a so_addr is contained in this range, + /// \b false otherwise. + //------------------------------------------------------------------ +// bool +// Contains (const Address &so_addr) const; + + //------------------------------------------------------------------ + /// Check if a section offset address is contained in this range. + /// + /// @param[in] so_addr_ptr + /// A section offset address object pointer. + /// + /// @return + /// Returns \b true if \a so_addr is contained in this range, + /// \b false otherwise. + //------------------------------------------------------------------ +// bool +// Contains (const Address *so_addr_ptr) const; + + //------------------------------------------------------------------ + /// Check if a section offset \a so_addr when represented as a file + /// address is contained within this object's file address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this and \a so_addr have + /// resolvable file address values and \a so_addr is contained + /// in the address range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsFileAddress (const Address &so_addr) const; + + //------------------------------------------------------------------ + /// Check if the resolved file address \a file_addr is contained + /// within this object's file address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this has a resolvable file + /// address value and \a so_addr is contained in the address + /// range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsFileAddress (lldb::addr_t file_addr) const; + + //------------------------------------------------------------------ + /// Check if a section offset \a so_addr when represented as a load + /// address is contained within this object's load address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this and \a so_addr have + /// resolvable load address values and \a so_addr is contained + /// in the address range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsLoadAddress (const Address &so_addr, Target *target) const; + + //------------------------------------------------------------------ + /// Check if the resolved load address \a load_addr is contained + /// within this object's load address range. + /// + /// @param[in] so_addr + /// A section offset address object reference. + /// + /// @return + /// Returns \b true if both \a this has a resolvable load + /// address value and \a so_addr is contained in the address + /// range, \b false otherwise. + //------------------------------------------------------------------ + bool + ContainsLoadAddress (lldb::addr_t load_addr, Target *target) const; + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. There are many ways to display a section + /// offset based address range, and \a style lets the user choose + /// how the base address gets displayed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] style + /// The display style for the address. + /// + /// @return + /// Returns \b true if the address was able to be displayed. + /// File and load addresses may be unresolved and it may not be + /// possible to display a valid value, \b false will be returned + /// in such cases. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + bool + Dump (Stream *s, Target *target, Address::DumpStyle style, Address::DumpStyle fallback_style = Address::DumpStyleInvalid) const; + + //------------------------------------------------------------------ + /// Dump a debug description of this object to a Stream. + /// + /// Dump a debug description of the contents of this object to the + /// supplied stream \a s. + /// + /// The debug description contains verbose internal state such + /// and pointer values, reference counts, etc. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + DumpDebug (Stream *s) const; + + //------------------------------------------------------------------ + /// Get accessor for the base address of the range. + /// + /// @return + /// A reference to the base address object. + //------------------------------------------------------------------ + Address & + GetBaseAddress() { return m_base_addr; } + + //------------------------------------------------------------------ + /// Get const accessor for the base address of the range. + /// + /// @return + /// A const reference to the base address object. + //------------------------------------------------------------------ + const Address & + GetBaseAddress() const { return m_base_addr; } + + //------------------------------------------------------------------ + /// Get accessor for the byte size of this range. + /// + /// @return + /// The size in bytes of this address range. + //------------------------------------------------------------------ + lldb::addr_t + GetByteSize () const { return m_byte_size; } + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize () const { + // Noting special for the memory size of a single AddressRange object, + // it is just the size of itself. + return sizeof(AddressRange); + } + + //------------------------------------------------------------------ + /// Set accessor for the byte size of this range. + /// + /// @param[in] byte_size + /// The new size in bytes of this address range. + //------------------------------------------------------------------ + void + SetByteSize (lldb::addr_t byte_size) { m_byte_size = byte_size; } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Address m_base_addr; ///< The section offset base address of this range. + lldb::addr_t m_byte_size; ///< The size in bytes of this address range. +}; + +//bool operator== (const AddressRange& lhs, const AddressRange& rhs); + +} // namespace lldb_private + +#endif // liblldb_AddressRange_h_ diff --git a/include/lldb/Core/AddressResolver.h b/include/lldb/Core/AddressResolver.h new file mode 100644 index 00000000000..e5fe276e3fb --- /dev/null +++ b/include/lldb/Core/AddressResolver.h @@ -0,0 +1,89 @@ +//===-- AddressResolver.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressResolver_h_ +#define liblldb_AddressResolver_h_ + +#include + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/ConstString.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressResolver AddressResolver.h "lldb/Core/AddressResolver.h" +/// @brief This class works with SearchFilter to resolve function names and +/// source file locations to their concrete addresses. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// The AddressResolver is a Searcher. In that protocol, +/// the SearchFilter asks the question "At what depth of the symbol context +/// descent do you want your callback to get called?" of the filter. The resolver +/// answers this question (in the GetDepth method) and provides the resolution callback. +//---------------------------------------------------------------------- + +class AddressResolver : + public Searcher +{ +public: + + typedef enum + { + Exact, + Regexp, + Glob + } MatchType; + + + AddressResolver (); + + virtual + ~AddressResolver (); + + virtual void + ResolveAddress (SearchFilter &filter); + + virtual void + ResolveAddressInModules (SearchFilter &filter, + ModuleList &modules); + + virtual void + GetDescription (Stream *s) = 0; + + std::vector & + GetAddressRanges (); + + size_t + GetNumberOfAddresses (); + + AddressRange & + GetAddressRangeAtIndex (size_t idx); + +protected: + + std::vector m_address_ranges; + +private: + DISALLOW_COPY_AND_ASSIGN(AddressResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_AddressResolver_h_ diff --git a/include/lldb/Core/AddressResolverFileLine.h b/include/lldb/Core/AddressResolverFileLine.h new file mode 100644 index 00000000000..ddeb0e0301d --- /dev/null +++ b/include/lldb/Core/AddressResolverFileLine.h @@ -0,0 +1,59 @@ +//===-- AddressResolverFileLine.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressResolverFileLine_h_ +#define liblldb_AddressResolverFileLine_h_ + +// Project includes +#include "lldb/Core/AddressResolver.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressResolverFileLine AddressResolverFileLine.h "lldb/Core/AddressResolverFileLine.h" +/// @brief This class finds address for source file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class AddressResolverFileLine : + public AddressResolver +{ +public: + + AddressResolverFileLine (const FileSpec &resolver, + uint32_t line_no, + bool check_inlines); + + virtual + ~AddressResolverFileLine (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + +protected: + FileSpec m_file_spec; // This is the file spec we are looking for. + uint32_t m_line_number; // This is the line number that we are looking for. + bool m_inlines; // This determines whether the resolver looks for inlined functions or not. + +private: + DISALLOW_COPY_AND_ASSIGN(AddressResolverFileLine); +}; + +} // namespace lldb_private + +#endif // liblldb_AddressResolverFileLine_h_ diff --git a/include/lldb/Core/AddressResolverName.h b/include/lldb/Core/AddressResolverName.h new file mode 100644 index 00000000000..afde675a89b --- /dev/null +++ b/include/lldb/Core/AddressResolverName.h @@ -0,0 +1,68 @@ +//===-- AddressResolverName.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AddressResolverName_h_ +#define liblldb_AddressResolverName_h_ + +// Project includes + +#include "lldb/Core/AddressResolver.h" +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class AddressResolverName AddressResolverName.h "lldb/Core/AddressResolverName.h" +/// @brief This class finds addresses for a given function name, either by exact match +/// or by regular expression. +//---------------------------------------------------------------------- + +class AddressResolverName: + public AddressResolver +{ +public: + + AddressResolverName (const char *func_name, + AddressResolver::MatchType type = Exact); + + // Creates a function breakpoint by regular expression. Takes over control of the lifespan of func_regex. + AddressResolverName (RegularExpression &func_regex); + + AddressResolverName (const char *class_name, + const char *method, + AddressResolver::MatchType type); + + virtual + ~AddressResolverName (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + +protected: + ConstString m_func_name; + ConstString m_class_name; // FIXME: Not used yet. The idea would be to stop on methods of this class. + RegularExpression m_regex; + AddressResolver::MatchType m_match_type; + +private: + DISALLOW_COPY_AND_ASSIGN(AddressResolverName); +}; + +} // namespace lldb_private + +#endif // liblldb_AddressResolverName_h_ diff --git a/include/lldb/Core/ArchSpec.h b/include/lldb/Core/ArchSpec.h new file mode 100644 index 00000000000..3bfa96be0ce --- /dev/null +++ b/include/lldb/Core/ArchSpec.h @@ -0,0 +1,422 @@ +//===-- ArchSpec.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ArchSpec_h_ +#define liblldb_ArchSpec_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" + +namespace lldb_private { + +struct CoreDefinition; + +//---------------------------------------------------------------------- +/// @class ArchSpec ArchSpec.h "lldb/Core/ArchSpec.h" +/// @brief An architecture specification class. +/// +/// A class designed to be created from a cpu type and subtype, a +/// string representation, or an llvm::Triple. Keeping all of the +/// conversions of strings to architecture enumeration values confined +/// to this class allows new architecture support to be added easily. +//---------------------------------------------------------------------- +class ArchSpec +{ +public: + enum Core + { + eCore_arm_generic, + eCore_arm_armv4, + eCore_arm_armv4t, + eCore_arm_armv5, + eCore_arm_armv5e, + eCore_arm_armv5t, + eCore_arm_armv6, + eCore_arm_armv7, + eCore_arm_armv7f, + eCore_arm_armv7s, + eCore_arm_armv7k, + eCore_arm_armv7m, + eCore_arm_armv7em, + eCore_arm_xscale, + eCore_thumb, + eCore_thumbv4t, + eCore_thumbv5, + eCore_thumbv5e, + eCore_thumbv6, + eCore_thumbv7, + eCore_thumbv7f, + eCore_thumbv7s, + eCore_thumbv7k, + eCore_thumbv7m, + eCore_thumbv7em, + + eCore_ppc_generic, + eCore_ppc_ppc601, + eCore_ppc_ppc602, + eCore_ppc_ppc603, + eCore_ppc_ppc603e, + eCore_ppc_ppc603ev, + eCore_ppc_ppc604, + eCore_ppc_ppc604e, + eCore_ppc_ppc620, + eCore_ppc_ppc750, + eCore_ppc_ppc7400, + eCore_ppc_ppc7450, + eCore_ppc_ppc970, + + eCore_ppc64_generic, + eCore_ppc64_ppc970_64, + + eCore_sparc_generic, + + eCore_sparc9_generic, + + eCore_x86_32_i386, + eCore_x86_32_i486, + eCore_x86_32_i486sx, + + eCore_x86_64_x86_64, + eCore_uknownMach32, + eCore_uknownMach64, + kNumCores, + + kCore_invalid, + // The following constants are used for wildcard matching only + kCore_any, + kCore_arm_any, + kCore_ppc_any, + kCore_ppc64_any, + kCore_x86_32_any, + + kCore_arm_first = eCore_arm_generic, + kCore_arm_last = eCore_arm_xscale, + + kCore_thumb_first = eCore_thumb, + kCore_thumb_last = eCore_thumbv7em, + + kCore_ppc_first = eCore_ppc_generic, + kCore_ppc_last = eCore_ppc_ppc970, + + kCore_ppc64_first = eCore_ppc64_generic, + kCore_ppc64_last = eCore_ppc64_ppc970_64, + + kCore_x86_32_first = eCore_x86_32_i386, + kCore_x86_32_last = eCore_x86_32_i486sx + }; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Default constructor that initializes the object with invalid + /// cpu type and subtype values. + //------------------------------------------------------------------ + ArchSpec (); + + //------------------------------------------------------------------ + /// Constructor over triple. + /// + /// Constructs an ArchSpec with properties consistent with the given + /// Triple. + //------------------------------------------------------------------ + explicit + ArchSpec (const llvm::Triple &triple); + explicit + ArchSpec (const char *triple_cstr); + explicit + ArchSpec (const char *triple_cstr, Platform *platform); + //------------------------------------------------------------------ + /// Constructor over architecture name. + /// + /// Constructs an ArchSpec with properties consistent with the given + /// object type and architecture name. + //------------------------------------------------------------------ + explicit + ArchSpec (ArchitectureType arch_type, + uint32_t cpu_type, + uint32_t cpu_subtype); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~ArchSpec (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] rhs another ArchSpec object to copy. + /// + /// @return A const reference to this object. + //------------------------------------------------------------------ + const ArchSpec& + operator= (const ArchSpec& rhs); + + static size_t + AutoComplete (const char *name, + StringList &matches); + + //------------------------------------------------------------------ + /// Returns a static string representing the current architecture. + /// + /// @return A static string correcponding to the current + /// architecture. + //------------------------------------------------------------------ + const char * + GetArchitectureName () const; + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object state back to a default invalid state. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Returns the size in bytes of an address of the current + /// architecture. + /// + /// @return The byte size of an address of the current architecture. + //------------------------------------------------------------------ + uint32_t + GetAddressByteSize () const; + + //------------------------------------------------------------------ + /// Returns a machine family for the current architecture. + /// + /// @return An LLVM arch type. + //------------------------------------------------------------------ + llvm::Triple::ArchType + GetMachine () const; + + //------------------------------------------------------------------ + /// Tests if this ArchSpec is valid. + /// + /// @return True if the current architecture is valid, false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid () const + { + return m_core >= eCore_arm_generic && m_core < kNumCores; + } + + bool + TripleVendorWasSpecified() const + { + return !m_triple.getVendorName().empty(); + } + + bool + TripleOSWasSpecified() const + { + return !m_triple.getOSName().empty(); + } + + //------------------------------------------------------------------ + /// Sets this ArchSpec according to the given architecture name. + /// + /// The architecture name can be one of the generic system default + /// values: + /// + /// @li \c LLDB_ARCH_DEFAULT - The arch the current system defaults + /// to when a program is launched without any extra + /// attributes or settings. + /// @li \c LLDB_ARCH_DEFAULT_32BIT - The default host architecture + /// for 32 bit (if any). + /// @li \c LLDB_ARCH_DEFAULT_64BIT - The default host architecture + /// for 64 bit (if any). + /// + /// Alternatively, if the object type of this ArchSpec has been + /// configured, a concrete architecture can be specified to set + /// the CPU type ("x86_64" for example). + /// + /// Finally, an encoded object and archetecture format is accepted. + /// The format contains an object type (like "macho" or "elf"), + /// followed by a platform dependent encoding of CPU type and + /// subtype. For example: + /// + /// "macho" : Specifies an object type of MachO. + /// "macho-16-6" : MachO specific encoding for ARMv6. + /// "elf-43 : ELF specific encoding for Sparc V9. + /// + /// @param[in] arch_name The name of an architecture. + /// + /// @return True if @p arch_name was successfully translated, false + /// otherwise. + //------------------------------------------------------------------ +// bool +// SetArchitecture (const llvm::StringRef& arch_name); +// +// bool +// SetArchitecture (const char *arch_name); + + //------------------------------------------------------------------ + /// Change the architecture object type and CPU type. + /// + /// @param[in] arch_type The object type of this ArchSpec. + /// + /// @param[in] cpu The required CPU type. + /// + /// @return True if the object and CPU type were sucessfully set. + //------------------------------------------------------------------ + bool + SetArchitecture (ArchitectureType arch_type, + uint32_t cpu, + uint32_t sub); + + //------------------------------------------------------------------ + /// Returns the byte order for the architecture specification. + /// + /// @return The endian enumeration for the current endianness of + /// the architecture specification + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder () const; + + //------------------------------------------------------------------ + /// Sets this ArchSpec's byte order. + /// + /// In the common case there is no need to call this method as the + /// byte order can almost always be determined by the architecture. + /// However, many CPU's are bi-endian (ARM, Alpha, PowerPC, etc) + /// and the default/assumed byte order may be incorrect. + //------------------------------------------------------------------ + void + SetByteOrder (lldb::ByteOrder byte_order) + { + m_byte_order = byte_order; + } + + uint32_t + GetMinimumOpcodeByteSize() const; + + uint32_t + GetMaximumOpcodeByteSize() const; + + Core + GetCore () const + { + return m_core; + } + + uint32_t + GetMachOCPUType () const; + + uint32_t + GetMachOCPUSubType () const; + + //------------------------------------------------------------------ + /// Architecture tripple accessor. + /// + /// @return A triple describing this ArchSpec. + //------------------------------------------------------------------ + llvm::Triple & + GetTriple () + { + return m_triple; + } + + //------------------------------------------------------------------ + /// Architecture tripple accessor. + /// + /// @return A triple describing this ArchSpec. + //------------------------------------------------------------------ + const llvm::Triple & + GetTriple () const + { + return m_triple; + } + + //------------------------------------------------------------------ + /// Architecture tripple setter. + /// + /// Configures this ArchSpec according to the given triple. If the + /// triple has unknown components in all of the vendor, OS, and + /// the optional environment field (i.e. "i386-unknown-unknown") + /// then default values are taken from the host. Architecture and + /// environment components are used to further resolve the CPU type + /// and subtype, endian characteristics, etc. + /// + /// @return A triple describing this ArchSpec. + //------------------------------------------------------------------ + bool + SetTriple (const llvm::Triple &triple); + + bool + SetTriple (const char *triple_cstr); + + bool + SetTriple (const char *triple_cstr, + Platform *platform); + + //------------------------------------------------------------------ + /// Returns the default endianness of the architecture. + /// + /// @return The endian enumeration for the default endianness of + /// the architecture. + //------------------------------------------------------------------ + lldb::ByteOrder + GetDefaultEndian () const; + + //------------------------------------------------------------------ + /// Compare an ArchSpec to another ArchSpec, requiring an exact cpu + /// type match between them. + /// e.g. armv7s is not an exact match with armv7 - this would return false + /// + /// @return true if the two ArchSpecs match. + //------------------------------------------------------------------ + bool + IsExactMatch (const ArchSpec& rhs) const; + + //------------------------------------------------------------------ + /// Compare an ArchSpec to another ArchSpec, requiring a compatible + /// cpu type match between them. + /// e.g. armv7s is compatible with armv7 - this method would return true + /// + /// @return true if the two ArchSpecs are compatible + //------------------------------------------------------------------ + bool + IsCompatibleMatch (const ArchSpec& rhs) const; + +protected: + bool + IsEqualTo (const ArchSpec& rhs, bool exact_match) const; + + llvm::Triple m_triple; + Core m_core; + lldb::ByteOrder m_byte_order; + + // Called when m_def or m_entry are changed. Fills in all remaining + // members with default values. + void + CoreUpdated (bool update_triple); +}; + +//------------------------------------------------------------------ +/// @fn bool operator< (const ArchSpec& lhs, const ArchSpec& rhs) +/// @brief Less than operator. +/// +/// Tests two ArchSpec objects to see if \a lhs is less than \a +/// rhs. +/// +/// @param[in] lhs The Left Hand Side ArchSpec object to compare. +/// @param[in] rhs The Left Hand Side ArchSpec object to compare. +/// +/// @return true if \a lhs is less than \a rhs +//------------------------------------------------------------------ +bool operator< (const ArchSpec& lhs, const ArchSpec& rhs); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_ArchSpec_h_ diff --git a/include/lldb/Core/Baton.h b/include/lldb/Core/Baton.h new file mode 100644 index 00000000000..627e7203a4a --- /dev/null +++ b/include/lldb/Core/Baton.h @@ -0,0 +1,62 @@ +//===-- Baton.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Baton_h_ +#define lldb_Baton_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Baton Baton.h "lldb/Core/Baton.h" +/// @brief A class designed to wrap callback batons so they can cleanup +/// any acquired resources +/// +/// This class is designed to be used by any objects that have a +/// callback function that takes a baton where the baton might need to +/// free/delete/close itself. +/// +/// The default behavior is to not free anything. Subclasses can +/// free any needed resources in their destructors. +//---------------------------------------------------------------------- +class Baton +{ +public: + explicit Baton(void *p) : + m_data (p) + { + } + + virtual + ~Baton() + { + // The default destructor for a baton does NOT attempt to clean up + // anything in m_baton + } + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + void *m_data; // Leave baton public for easy access + +private: + //------------------------------------------------------------------ + // For Baton only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Baton); +}; + +} // namespace lldb_private + +#endif // lldb_Baton_h_ diff --git a/include/lldb/Core/Broadcaster.h b/include/lldb/Core/Broadcaster.h new file mode 100644 index 00000000000..64b12ca8a93 --- /dev/null +++ b/include/lldb/Core/Broadcaster.h @@ -0,0 +1,475 @@ +//===-- Broadcaster.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Broadcaster_h_ +#define liblldb_Broadcaster_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +//#include "lldb/Core/Flags.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Listener.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// lldb::BroadcastEventSpec +// +// This class is used to specify a kind of event to register for. The Debugger +// maintains a list of BroadcastEventSpec's and when it is made +//---------------------------------------------------------------------- +class BroadcastEventSpec +{ +public: + BroadcastEventSpec (const ConstString &broadcaster_class, uint32_t event_bits) : + m_broadcaster_class (broadcaster_class), + m_event_bits (event_bits) + { + } + + BroadcastEventSpec (const BroadcastEventSpec &rhs); + + ~BroadcastEventSpec() {} + + const ConstString &GetBroadcasterClass() const + { + return m_broadcaster_class; + } + + uint32_t GetEventBits () const + { + return m_event_bits; + } + + // Tell whether this BroadcastEventSpec is contained in in_spec. + // That is: + // (a) the two spec's share the same broadcaster class + // (b) the event bits of this spec are wholly contained in those of in_spec. + bool IsContainedIn (BroadcastEventSpec in_spec) const + { + if (m_broadcaster_class != in_spec.GetBroadcasterClass()) + return false; + uint32_t in_bits = in_spec.GetEventBits(); + if (in_bits == m_event_bits) + return true; + else + { + if ((m_event_bits & in_bits) != 0 + && (m_event_bits & ~in_bits) == 0) + return true; + } + return false; + } + + bool operator< (const BroadcastEventSpec &rhs) const; + const BroadcastEventSpec &operator= (const BroadcastEventSpec &rhs); + +private: + ConstString m_broadcaster_class; + uint32_t m_event_bits; +}; + +class BroadcasterManager +{ +public: + friend class Listener; + + BroadcasterManager (); + + ~BroadcasterManager () {} + + uint32_t + RegisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec); + + bool + UnregisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec); + + Listener * + GetListenerForEventSpec (BroadcastEventSpec event_spec) const; + + void + SignUpListenersForBroadcaster (Broadcaster &broadcaster); + + void + RemoveListener (Listener &Listener); + +protected: + void Clear(); + +private: + typedef std::pair event_listener_key; + typedef std::map collection; + typedef std::set listener_collection; + collection m_event_map; + listener_collection m_listeners; + + Mutex m_manager_mutex; + + // A couple of comparator classes for find_if: + + class BroadcasterClassMatches + { + public: + BroadcasterClassMatches (const ConstString &broadcaster_class) : + m_broadcaster_class (broadcaster_class) + { + } + + ~BroadcasterClassMatches () {} + + bool operator() (const event_listener_key input) const + { + return (input.first.GetBroadcasterClass() == m_broadcaster_class); + } + + private: + ConstString m_broadcaster_class; + }; + + class BroadcastEventSpecMatches + { + public: + BroadcastEventSpecMatches (BroadcastEventSpec broadcaster_spec) : + m_broadcaster_spec (broadcaster_spec) + { + } + + ~BroadcastEventSpecMatches () {} + + bool operator() (const event_listener_key input) const + { + return (input.first.IsContainedIn (m_broadcaster_spec)); + } + + private: + BroadcastEventSpec m_broadcaster_spec; + }; + + class ListenerMatchesAndSharedBits + { + public: + ListenerMatchesAndSharedBits (BroadcastEventSpec broadcaster_spec, + const Listener &listener) : + m_broadcaster_spec (broadcaster_spec), + m_listener (&listener) + { + } + + ~ListenerMatchesAndSharedBits () {} + + bool operator() (const event_listener_key input) const + { + return (input.first.GetBroadcasterClass() == m_broadcaster_spec.GetBroadcasterClass() + && (input.first.GetEventBits() & m_broadcaster_spec.GetEventBits()) != 0 + && input.second == m_listener); + } + + private: + BroadcastEventSpec m_broadcaster_spec; + const Listener *m_listener; + }; + + class ListenerMatches + { + public: + ListenerMatches (const Listener &in_listener) : + m_listener (&in_listener) + { + } + + ~ListenerMatches() {} + + bool operator () (const event_listener_key input) const + { + if (input.second == m_listener) + return true; + else + return false; + } + + private: + const Listener *m_listener; + + }; + +}; + +//---------------------------------------------------------------------- +/// @class Broadcaster Broadcaster.h "lldb/Core/Broadcaster.h" +/// @brief An event broadcasting class. +/// +/// The Broadcaster class is designed to be subclassed by objects that +/// wish to vend events in a multi-threaded environment. Broadcaster +/// objects can each vend 32 events. Each event is represented by a bit +/// in a 32 bit value and these bits can be set: +/// @see Broadcaster::SetEventBits(uint32_t) +/// or cleared: +/// @see Broadcaster::ResetEventBits(uint32_t) +/// When an event gets set the Broadcaster object will notify the +/// Listener object that is listening for the event (if there is one). +/// +/// Subclasses should provide broadcast bit definitions for any events +/// they vend, typically using an enumeration: +/// \code +/// class Foo : public Broadcaster +/// { +/// public: +/// //---------------------------------------------------------- +/// // Broadcaster event bits definitions. +/// //---------------------------------------------------------- +/// enum +/// { +/// eBroadcastBitStateChanged = (1 << 0), +/// eBroadcastBitInterrupt = (1 << 1), +/// eBroadcastBitSTDOUT = (1 << 2), +/// eBroadcastBitSTDERR = (1 << 3), +/// eBroadcastBitProfileData = (1 << 4) +/// }; +/// \endcode +//---------------------------------------------------------------------- +class Broadcaster +{ +public: + //------------------------------------------------------------------ + /// Construct with a broadcaster with a name. + /// + /// @param[in] name + /// A NULL terminated C string that contains the name of the + /// broadcaster object. + //------------------------------------------------------------------ + Broadcaster (BroadcasterManager *manager, const char *name); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class gets subclassed. + //------------------------------------------------------------------ + virtual + ~Broadcaster(); + + void + CheckInWithManager (); + + //------------------------------------------------------------------ + /// Broadcast an event which has no associated data. + /// + /// @param[in] event_type + /// The element from the enum defining this broadcaster's events + /// that is being broadcast. + /// + /// @param[in] event_data + /// User event data that will be owned by the lldb::Event that + /// is created internally. + /// + /// @param[in] unique + /// If true, then only add an event of this type if there isn't + /// one already in the queue. + /// + //------------------------------------------------------------------ + void + BroadcastEvent (lldb::EventSP &event_sp); + + void + BroadcastEventIfUnique (lldb::EventSP &event_sp); + + void + BroadcastEvent (uint32_t event_type, EventData *event_data = NULL); + + void + BroadcastEventIfUnique (uint32_t event_type, EventData *event_data = NULL); + + void + Clear(); + + virtual void + AddInitialEventsToListener (Listener *listener, uint32_t requested_events); + + //------------------------------------------------------------------ + /// Listen for any events specified by \a event_mask. + /// + /// Only one listener can listen to each event bit in a given + /// Broadcaster. Once a listener has acquired an event bit, no + /// other broadcaster will have access to it until it is + /// relinquished by the first listener that gets it. The actual + /// event bits that get acquired by \a listener may be different + /// from what is requested in \a event_mask, and to track this the + /// actual event bits that are acquired get returned. + /// + /// @param[in] listener + /// The Listener object that wants to monitor the events that + /// get broadcast by this object. + /// + /// @param[in] event_mask + /// A bit mask that indicates which events the listener is + /// asking to monitor. + /// + /// @return + /// The actual event bits that were acquired by \a listener. + //------------------------------------------------------------------ + uint32_t + AddListener (Listener* listener, uint32_t event_mask); + + //------------------------------------------------------------------ + /// Get the NULL terminated C string name of this Broadcaster + /// object. + /// + /// @return + /// The NULL terminated C string name of this Broadcaster. + //------------------------------------------------------------------ + const ConstString & + GetBroadcasterName (); + + + //------------------------------------------------------------------ + /// Get the event name(s) for one or more event bits. + /// + /// @param[in] event_mask + /// A bit mask that indicates which events to get names for. + /// + /// @return + /// The NULL terminated C string name of this Broadcaster. + //------------------------------------------------------------------ + bool + GetEventNames (Stream &s, const uint32_t event_mask, bool prefix_with_broadcaster_name) const; + + //------------------------------------------------------------------ + /// Set the name for an event bit. + /// + /// @param[in] event_mask + /// A bit mask that indicates which events the listener is + /// asking to monitor. + /// + /// @return + /// The NULL terminated C string name of this Broadcaster. + //------------------------------------------------------------------ + void + SetEventName (uint32_t event_mask, const char *name) + { + m_event_names[event_mask] = name; + } + + const char * + GetEventName (uint32_t event_mask) const + { + event_names_map::const_iterator pos = m_event_names.find (event_mask); + if (pos != m_event_names.end()) + return pos->second.c_str(); + return NULL; + } + + bool + EventTypeHasListeners (uint32_t event_type); + + //------------------------------------------------------------------ + /// Removes a Listener from this broadcasters list and frees the + /// event bits specified by \a event_mask that were previously + /// acquired by \a listener (assuming \a listener was listening to + /// this object) for other listener objects to use. + /// + /// @param[in] listener + /// A Listener object that previously called AddListener. + /// + /// @param[in] event_mask + /// The event bits \a listener wishes to relinquish. + /// + /// @return + /// \b True if the listener was listening to this broadcaster + /// and was removed, \b false otherwise. + /// + /// @see uint32_t Broadcaster::AddListener (Listener*, uint32_t) + //------------------------------------------------------------------ + bool + RemoveListener (Listener* listener, uint32_t event_mask = UINT32_MAX); + + //------------------------------------------------------------------ + /// Provides a simple mechanism to temporarily redirect events from + /// broadcaster. When you call this function passing in a listener and + /// event type mask, all events from the broadcaster matching the mask + /// will now go to the hijacking listener. + /// Only one hijack can occur at a time. If we need more than this we + /// will have to implement a Listener stack. + /// + /// @param[in] listener + /// A Listener object. You do not need to call StartListeningForEvents + /// for this broadcaster (that would fail anyway since the event bits + /// would most likely be taken by the listener(s) you are usurping. + /// + /// @param[in] event_mask + /// The event bits \a listener wishes to hijack. + /// + /// @return + /// \b True if the event mask could be hijacked, \b false otherwise. + /// + /// @see uint32_t Broadcaster::AddListener (Listener*, uint32_t) + //------------------------------------------------------------------ + bool + HijackBroadcaster (Listener *listener, uint32_t event_mask = UINT32_MAX); + + bool + IsHijackedForEvent (uint32_t event_mask) + { + if (m_hijacking_listeners.size() > 0) + return (event_mask & m_hijacking_masks.back()) != 0; + return false; + } + + //------------------------------------------------------------------ + /// Restore the state of the Broadcaster from a previous hijack attempt. + /// + //------------------------------------------------------------------ + void + RestoreBroadcaster (); + + // This needs to be filled in if you are going to register the broadcaster with the broadcaster + // manager and do broadcaster class matching. + // FIXME: Probably should make a ManagedBroadcaster subclass with all the bits needed to work + // with the BroadcasterManager, so that it is clearer how to add one. + virtual ConstString &GetBroadcasterClass() const; + + BroadcasterManager *GetManager(); + +protected: + + + void + PrivateBroadcastEvent (lldb::EventSP &event_sp, bool unique); + + //------------------------------------------------------------------ + // Classes that inherit from Broadcaster can see and modify these + //------------------------------------------------------------------ + typedef std::vector< std::pair > collection; + typedef std::map event_names_map; + // Prefix the name of our member variables with "m_broadcaster_" + // since this is a class that gets subclassed. + const ConstString m_broadcaster_name; ///< The name of this broadcaster object. + event_names_map m_event_names; ///< Optionally define event names for readability and logging for each event bit + collection m_listeners; ///< A list of Listener / event_mask pairs that are listening to this broadcaster. + Mutex m_listeners_mutex; ///< A mutex that protects \a m_listeners. + std::vector m_hijacking_listeners; // A simple mechanism to intercept events from a broadcaster + std::vector m_hijacking_masks; // At some point we may want to have a stack or Listener + // collections, but for now this is just for private hijacking. + BroadcasterManager *m_manager; + +private: + //------------------------------------------------------------------ + // For Broadcaster only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Broadcaster); +}; + +} // namespace lldb_private + +#endif // liblldb_Broadcaster_h_ diff --git a/include/lldb/Core/ClangForward.h b/include/lldb/Core/ClangForward.h new file mode 100644 index 00000000000..0b3f13a1660 --- /dev/null +++ b/include/lldb/Core/ClangForward.h @@ -0,0 +1,136 @@ +//===-- ClangForward.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangForward_h_ +#define liblldb_ClangForward_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#if defined(__cplusplus) + +namespace clang +{ + namespace Builtin + { + class Context; + } + + class Action; + class ASTConsumer; + class ASTContext; + class ASTRecordLayout; + class AddrLabelExpr; + class AnalyzerOptions; + class BinaryOperator; + class ClassTemplateDecl; + class ClassTemplateSpecializationDecl; + class CodeGenOptions; + class CodeGenerator; + class CompilerInstance; + class CompoundStmt; + class CXXBaseSpecifier; + class CXXBoolLiteralExpr; + class CXXFunctionalCastExpr; + class CXXMethodDecl; + class CXXNamedCastExpr; + class CXXRecordDecl; + class CXXThisExpr; + class CharacterLiteral; + class CompoundAssignOperator; + class Decl; + class DeclarationName; + class DeclaratorDecl; + class DeclContext; + class DeclRefExpr; + class DeclStmt; + class DependencyOutputOptions; + class Diagnostic; + class DiagnosticConsumer; + class DiagnosticsEngine; + class DiagnosticOptions; + class EnumDecl; + class Expr; + class ExternalASTSource; + class ExtVectorElementExpr; + class FieldDecl; + class FileManager; + class FileSystemOptions; + class FloatingLiteral; + class FrontendOptions; + class FunctionDecl; + class FunctionTemplateDecl; + class FunctionTemplateSpecializationInfo; + class GotoStmt; + class HeaderSearchOptions; + class IdentifierTable; + class IntegerLiteral; + class LabelStmt; + class LangOptions; + class MemberExpr; + class NamedDecl; + class NamespaceDecl; + class NonTypeTemplateParmDecl; + class ObjCEncodeExpr; + class ObjCImplicitSetterGetterRefExpr; + class ObjCInterfaceDecl; + class ObjCIvarDecl; + class ObjCIvarRefExpr; + class ObjCMessageExpr; + class ObjCMethodDecl; + class ObjCPropertyRefExpr; + class ObjCProtocolDecl; + class ObjCProtocolExpr; + class ObjCSelectorExpr; + class ObjCSuperExpr; + class ParenExpr; + class ParmVarDecl; + class PredefinedExpr; + class PreprocessorOptions; + class PreprocessorOutputOptions; + class QualType; + class QualifiedNameType; + class RecordDecl; + class SelectorTable; + class SizeOfAlignOfExpr; + class SourceLocation; + class SourceManager; + class Stmt; + class StmtIteratorBase; + class StringLiteral; + class TagDecl; + class TargetInfo; + class TargetOptions; + class TemplateArgument; + class TemplateDecl; + class TemplateParameterList; + class TemplateTemplateParmDecl; + class TemplateTypeParmDecl; + class TextDiagnosticBuffer; + class TranslationUnitDecl; + class Type; + class TypeDecl; + class TypedefDecl; + class TypesCompatibleExpr; + class UnaryOperator; + class ValueDecl; + class VarDecl; + struct PrintingPolicy; +} + +namespace llvm +{ + class LLVMContext; + class ExecutionEngine; +} + +#endif // #if defined(__cplusplus) +#endif // liblldb_ClangForward_h_ diff --git a/include/lldb/Core/Communication.h b/include/lldb/Core/Communication.h new file mode 100644 index 00000000000..98d4dfd011b --- /dev/null +++ b/include/lldb/Core/Communication.h @@ -0,0 +1,413 @@ +//===-- Communication.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Communication_h_ +#define liblldb_Communication_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Mutex.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Communication Communication.h "lldb/Core/Communication.h" +/// @brief An abstract communications class. +/// +/// Communication is an class that handles data communication +/// between two data sources. It uses a Connection class to do the +/// real communication. This approach has a couple of advantages: it +/// allows a single instance of this class to be used even though its +/// connection can change. Connections could negotiate for different +/// connections based on abilities like starting with Bluetooth and +/// negotiating up to WiFi if available. It also allows this class to be +/// subclassed by any interfaces that don't want to give bytes but want +/// to validate and give out packets. This can be done by overriding: +/// +/// AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast); +/// +/// Communication inherits from Broadcaster which means it can be +/// used in conjunction with Listener to wait for multiple broadcaster +/// objects and multiple events from each of those objects. +/// Communication defines a set of pre-defined event bits (see +/// enumerations definitions that start with "eBroadcastBit" below). +/// +/// There are two modes in which communications can occur: +/// @li single-threaded +/// @li multi-threaded +/// +/// In single-threaded mode, all reads and writes happen synchronously +/// on the calling thread. +/// +/// In multi-threaded mode, a read thread is spawned that continually +/// reads data and caches any received bytes. To start the read thread +/// clients call: +/// +/// bool Communication::StartReadThread (Error *); +/// +/// If true is returned a read thead has been spawned that will +/// continually execute a call to the pure virtual DoRead function: +/// +/// size_t Communication::ReadFromConnection (void *, size_t, uint32_t); +/// +/// When bytes are received the data gets cached in \a m_bytes and this +/// class will broadcast a \b eBroadcastBitReadThreadGotBytes event. +/// Clients that want packet based communication should override +/// AppendBytesToCache. The subclasses can choose to call the +/// built in AppendBytesToCache with the \a broadcast parameter set to +/// false. This will cause the \b eBroadcastBitReadThreadGotBytes event +/// not get broadcast, and then the subclass can post a \b +/// eBroadcastBitPacketAvailable event when a full packet of data has +/// been received. +/// +/// If the connection is disconnected a \b eBroadcastBitDisconnected +/// event gets broadcast. If the read thread exits a \b +/// eBroadcastBitReadThreadDidExit event will be broadcast. Clients +/// can also post a \b eBroadcastBitReadThreadShouldExit event to this +/// object which will cause the read thread to exit. +//---------------------------------------------------------------------- +class Communication : public Broadcaster +{ +public: + enum { + eBroadcastBitDisconnected = (1 << 0), ///< Sent when the communications connection is lost. + eBroadcastBitReadThreadGotBytes = (1 << 1), ///< Sent by the read thread when bytes become available. + eBroadcastBitReadThreadDidExit = (1 << 2), ///< Sent by the read thread when it exits to inform clients. + eBroadcastBitReadThreadShouldExit = (1 << 3), ///< Sent by clients that need to cancel the read thread. + eBroadcastBitPacketAvailable = (1 << 4), ///< Sent when data received makes a complete packet. + kLoUserBroadcastBit = (1 << 16),///< Subclasses can used bits 31:16 for any needed events. + kHiUserBroadcastBit = (1 << 31), + eAllEventBits = 0xffffffff + }; + + typedef void (*ReadThreadBytesReceived) (void *baton, const void *src, size_t src_len); + + + //------------------------------------------------------------------ + /// Construct the Communication object with the specified name for + /// the Broadcaster that this object inherits from. + /// + /// @param[in] broadcaster_name + /// The name of the broadcaster object. This name should be as + /// complete as possible to uniquely identify this object. The + /// broadcaster name can be updated after the connect function + /// is called. + //------------------------------------------------------------------ + Communication(const char * broadcaster_name); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class gets subclassed. + //------------------------------------------------------------------ + virtual + ~Communication(); + + void + Clear (); + + //------------------------------------------------------------------ + /// Connect using the current connection by passing \a url to its + /// connect function. + /// string. + /// + /// @param[in] url + /// A string that contains all information needed by the + /// subclass to connect to another client. + /// + /// @return + /// \b True if the connect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + /// @see bool Connection::Connect (const char *url); + //------------------------------------------------------------------ + lldb::ConnectionStatus + Connect (const char *url, Error *error_ptr); + + //------------------------------------------------------------------ + /// Disconnect the communications connection if one is currently + /// connected. + /// + /// @return + /// \b True if the disconnect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + /// @see bool Connection::Disconnect (); + //------------------------------------------------------------------ + lldb::ConnectionStatus + Disconnect (Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Check if the connection is valid. + /// + /// @return + /// \b True if this object is currently connected, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsConnected () const; + + bool + HasConnection () const; + + lldb_private::Connection * + GetConnection () + { + return m_connection_sp.get(); + } + //------------------------------------------------------------------ + /// Read bytes from the current connection. + /// + /// If no read thread is running, this function call the + /// connection's Connection::Read(...) function to get any available. + /// + /// If a read thread has been started, this function will check for + /// any cached bytes that have already been read and return any + /// currently available bytes. If no bytes are cached, it will wait + /// for the bytes to become available by listening for the \a + /// eBroadcastBitReadThreadGotBytes event. If this function consumes + /// all of the bytes in the cache, it will reset the + /// \a eBroadcastBitReadThreadGotBytes event bit. + /// + /// @param[in] dst + /// A destination buffer that must be at least \a dst_len bytes + /// long. + /// + /// @param[in] dst_len + /// The number of bytes to attempt to read, and also the max + /// number of bytes that can be placed into \a dst. + /// + /// @param[in] timeout_usec + /// A timeout value in micro-seconds. + /// + /// @return + /// The number of bytes actually read. + /// + /// @see size_t Connection::Read (void *, size_t); + //------------------------------------------------------------------ + size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + + //------------------------------------------------------------------ + /// The actual write function that attempts to write to the + /// communications protocol. + /// + /// Subclasses must override this function. + /// + /// @param[in] src + /// A source buffer that must be at least \a src_len bytes + /// long. + /// + /// @param[in] src_len + /// The number of bytes to attempt to write, and also the + /// number of bytes are currently available in \a src. + /// + /// @return + /// The number of bytes actually Written. + //------------------------------------------------------------------ + size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status, + Error *error_ptr); + + //------------------------------------------------------------------ + /// Sets the connection that it to be used by this class. + /// + /// By making a communication class that uses different connections + /// it allows a single communication interface to negotiate and + /// change its connection without any interruption to the client. + /// It also allows the Communication class to be subclassed for + /// packet based communication. + /// + /// @param[in] connection + /// A connection that this class will own and destroy. + /// + /// @see + /// class Connection + //------------------------------------------------------------------ + void + SetConnection (Connection *connection); + + //------------------------------------------------------------------ + /// Starts a read thread whose sole purpose it to read bytes from + /// the current connection. This function will call connection's + /// read function: + /// + /// size_t Connection::Read (void *, size_t); + /// + /// When bytes are read and cached, this function will call: + /// + /// Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast); + /// + /// Subclasses should override this function if they wish to override + /// the default action of caching the bytes and broadcasting a \b + /// eBroadcastBitReadThreadGotBytes event. + /// + /// @return + /// \b True if the read thread was successfully started, \b + /// false otherwise. + /// + /// @see size_t Connection::Read (void *, size_t); + /// @see void Communication::AppendBytesToCache (const uint8_t * bytes, size_t len, bool broadcast); + //------------------------------------------------------------------ + virtual bool + StartReadThread (Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Stops the read thread by cancelling it. + /// + /// @return + /// \b True if the read thread was successfully canceled, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + StopReadThread (Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Checks if there is a currently running read thread. + /// + /// @return + /// \b True if the read thread is running, \b false otherwise. + //------------------------------------------------------------------ + bool + ReadThreadIsRunning (); + + //------------------------------------------------------------------ + /// The static read thread function. This function will call + /// the "DoRead" function continuously and wait for data to become + /// avaialble. When data is received it will append the available + /// data to the internal cache and broadcast a + /// \b eBroadcastBitReadThreadGotBytes event. + /// + /// @param[in] comm_ptr + /// A pointer to an instance of this class. + /// + /// @return + /// \b NULL. + /// + /// @see void Communication::ReadThreadGotBytes (const uint8_t *, size_t); + //------------------------------------------------------------------ + static lldb::thread_result_t + ReadThread (lldb::thread_arg_t comm_ptr); + + void + SetReadThreadBytesReceivedCallback (ReadThreadBytesReceived callback, + void *callback_baton); + + static const char * + ConnectionStatusAsCString (lldb::ConnectionStatus status); + + bool + GetCloseOnEOF () const + { + return m_close_on_eof; + } + + void + SetCloseOnEOF (bool b) + { + m_close_on_eof = b; + } + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + +private: + //------------------------------------------------------------------ + // For Communication only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Communication); + + +protected: + lldb::ConnectionSP m_connection_sp; ///< The connection that is current in use by this communications class. + lldb::thread_t m_read_thread; ///< The read thread handle in case we need to cancel the thread. + bool m_read_thread_enabled; + std::string m_bytes; ///< A buffer to cache bytes read in the ReadThread function. + Mutex m_bytes_mutex; ///< A mutex to protect multi-threaded access to the cached bytes. + Mutex m_write_mutex; ///< Don't let multiple threads write at the same time... + ReadThreadBytesReceived m_callback; + void *m_callback_baton; + bool m_close_on_eof; + + size_t + ReadFromConnection (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + //------------------------------------------------------------------ + /// Append new bytes that get read from the read thread into the + /// internal object byte cache. This will cause a \b + /// eBroadcastBitReadThreadGotBytes event to be broadcast if \a + /// broadcast is true. + /// + /// Subclasses can override this function in order to inspect the + /// received data and check if a packet is available. + /// + /// Subclasses can also still call this function from the + /// overridden method to allow the caching to correctly happen and + /// suppress the broadcasting of the \a eBroadcastBitReadThreadGotBytes + /// event by setting \a broadcast to false. + /// + /// @param[in] src + /// A source buffer that must be at least \a src_len bytes + /// long. + /// + /// @param[in] src_len + /// The number of bytes to append to the cache. + //------------------------------------------------------------------ + virtual void + AppendBytesToCache (const uint8_t *src, size_t src_len, bool broadcast, lldb::ConnectionStatus status); + + //------------------------------------------------------------------ + /// Get any available bytes from our data cache. If this call + /// empties the data cache, the \b eBroadcastBitReadThreadGotBytes event + /// will be reset to signify no more bytes are available. + /// + /// @param[in] dst + /// A destination buffer that must be at least \a dst_len bytes + /// long. + /// + /// @param[in] dst_len + /// The number of bytes to attempt to read from the cache, + /// and also the max number of bytes that can be placed into + /// \a dst. + /// + /// @return + /// The number of bytes extracted from the data cache. + //------------------------------------------------------------------ + size_t + GetCachedBytes (void *dst, size_t dst_len); +}; + +} // namespace lldb_private + +#endif // liblldb_Communication_h_ diff --git a/include/lldb/Core/Connection.h b/include/lldb/Core/Connection.h new file mode 100644 index 00000000000..dde3c4b1022 --- /dev/null +++ b/include/lldb/Core/Connection.h @@ -0,0 +1,162 @@ +//===-- Connection.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Connection_h_ +#define liblldb_Connection_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Connection Connection.h "lldb/Core/Connection.h" +/// @brief A communication connection class. +/// +/// A class that implements that actual communication functions for +/// connecting/disconnecting, reading/writing, and waiting for bytes +/// to become available from a two way communication connection. +/// +/// This class is designed to only do very simple communication +/// functions. Instances can be instantiated and given to a +/// Communication class to perform communications where clients can +/// listen for broadcasts, and perform other higher level communications. +//---------------------------------------------------------------------- +class Connection +{ +public: + //------------------------------------------------------------------ + /// Default constructor + //------------------------------------------------------------------ + Connection (); + + //------------------------------------------------------------------ + /// Virtual destructor since this class gets subclassed and handed + /// to a Communication object. + //------------------------------------------------------------------ + virtual + ~Connection (); + + //------------------------------------------------------------------ + /// Connect using the connect string \a url. + /// + /// @param[in] url + /// A string that contains all information needed by the + /// subclass to connect to another client. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns false. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// \b True if the connect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + //------------------------------------------------------------------ + virtual lldb::ConnectionStatus + Connect (const char *url, Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// Disconnect the communications connection if one is currently + /// connected. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns false. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// \b True if the disconnect succeeded, \b false otherwise. The + /// internal error object should be filled in with an + /// appropriate value based on the result of this function. + /// + /// @see Error& Communication::GetError (); + //------------------------------------------------------------------ + virtual lldb::ConnectionStatus + Disconnect (Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// Check if the connection is valid. + /// + /// @return + /// \b True if this object is currently connected, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual bool + IsConnected () const = 0; + + //------------------------------------------------------------------ + /// The read function that attempts to read from the connection. + /// + /// @param[in] dst + /// A destination buffer that must be at least \a dst_len bytes + /// long. + /// + /// @param[in] dst_len + /// The number of bytes to attempt to read, and also the max + /// number of bytes that can be placed into \a dst. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns zero. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// The number of bytes actually read. + /// + /// @see size_t Communication::Read (void *, size_t, uint32_t); + //------------------------------------------------------------------ + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr) = 0; + + //------------------------------------------------------------------ + /// The actual write function that attempts to write to the + /// communications protocol. + /// + /// Subclasses must override this function. + /// + /// @param[in] src + /// A source buffer that must be at least \a src_len bytes + /// long. + /// + /// @param[in] src_len + /// The number of bytes to attempt to write, and also the + /// number of bytes are currently available in \a src. + /// + /// @param[out] error_ptr + /// A pointer to an error object that should be given an + /// approriate error value if this method returns zero. This + /// value can be NULL if the error value should be ignored. + /// + /// @return + /// The number of bytes actually Written. + //------------------------------------------------------------------ + virtual size_t + Write (const void *buffer, size_t length, lldb::ConnectionStatus &status, Error *error_ptr) = 0; + +private: + //------------------------------------------------------------------ + // For Connection only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Connection); +}; + +} // namespace lldb_private + +#endif // liblldb_Connection_h_ diff --git a/include/lldb/Core/ConnectionFileDescriptor.h b/include/lldb/Core/ConnectionFileDescriptor.h new file mode 100644 index 00000000000..fe704d4cadf --- /dev/null +++ b/include/lldb/Core/ConnectionFileDescriptor.h @@ -0,0 +1,139 @@ +//===-- ConnectionFileDescriptor.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ConnectionFileDescriptor_h_ +#define liblldb_ConnectionFileDescriptor_h_ + +// C Includes +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Host/SocketAddress.h" + +namespace lldb_private { + +class ConnectionFileDescriptor : + public Connection +{ +public: + + ConnectionFileDescriptor (); + + ConnectionFileDescriptor (int fd, bool owns_fd); + + virtual + ~ConnectionFileDescriptor (); + + virtual bool + IsConnected () const; + + virtual lldb::ConnectionStatus + Connect (const char *s, Error *error_ptr); + + virtual lldb::ConnectionStatus + Disconnect (Error *error_ptr); + + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + + virtual size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status, + Error *error_ptr); + + // If the read file descriptor is a socket, then return + // the port number that is being used by the socket. + in_port_t + GetReadPort () const; + + // If the write file descriptor is a socket, then return + // the port number that is being used by the socket. + in_port_t + GetWritePort () const; + +protected: + + void + OpenCommandPipe (); + + void + CloseCommandPipe (); + + lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, Error *error_ptr); + + lldb::ConnectionStatus + SocketListen (uint16_t listen_port_num, Error *error_ptr); + + lldb::ConnectionStatus + ConnectTCP (const char *host_and_port, Error *error_ptr); + + lldb::ConnectionStatus + ConnectUDP (const char *args, Error *error_ptr); + + lldb::ConnectionStatus + NamedSocketAccept (const char *socket_name, Error *error_ptr); + + lldb::ConnectionStatus + NamedSocketConnect (const char *socket_name, Error *error_ptr); + + lldb::ConnectionStatus + Close (int& fd, Error *error); + + typedef enum + { + eFDTypeFile, // Other FD requireing read/write + eFDTypeSocket, // Socket requiring send/recv + eFDTypeSocketUDP // Unconnected UDP socket requiring sendto/recvfrom + } FDType; + + int m_fd_send; + int m_fd_recv; + FDType m_fd_send_type; + FDType m_fd_recv_type; + SocketAddress m_udp_send_sockaddr; + bool m_should_close_fd; // True if this class should close the file descriptor when it goes away. + uint32_t m_socket_timeout_usec; + int m_pipe_read; // A pipe that we select on the reading end of along with + int m_pipe_write; // m_fd_recv so we can force ourselves out of the select. + Mutex m_mutex; + bool m_shutting_down; // This marks that we are shutting down so if we get woken up from BytesAvailable + // to disconnect, we won't try to read again. + + static in_port_t + GetSocketPort (int fd); + + static int + GetSocketOption(int fd, int level, int option_name, int &option_value); + + static int + SetSocketOption(int fd, int level, int option_name, int option_value); + + bool + SetSocketReceiveTimeout (uint32_t timeout_usec); + +private: + DISALLOW_COPY_AND_ASSIGN (ConnectionFileDescriptor); +}; + +} // namespace lldb_private + +#endif // liblldb_ConnectionFileDescriptor_h_ diff --git a/include/lldb/Core/ConnectionMachPort.h b/include/lldb/Core/ConnectionMachPort.h new file mode 100644 index 00000000000..5613e7ee800 --- /dev/null +++ b/include/lldb/Core/ConnectionMachPort.h @@ -0,0 +1,92 @@ +//===-- ConnectionMachPort.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#if defined(__APPLE__) + +#ifndef liblldb_ConnectionMachPort_h_ +#define liblldb_ConnectionMachPort_h_ + +// C Includes +#include + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" + +class ConnectionMachPort : + public lldb_private::Connection +{ +public: + ConnectionMachPort (); + + virtual + ~ConnectionMachPort (); + + virtual bool + IsConnected () const; + + virtual lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, lldb_private::Error *error_ptr); + + virtual lldb::ConnectionStatus + Connect (const char *s, lldb_private::Error *error_ptr); + + virtual lldb::ConnectionStatus + Disconnect (lldb_private::Error *error_ptr); + + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + lldb_private::Error *error_ptr); + + virtual size_t + Write (const void *src, + size_t src_len, + lldb::ConnectionStatus &status, + lldb_private::Error *error_ptr); + + lldb::ConnectionStatus + BootstrapCheckIn (const char *port_name, + lldb_private::Error *error_ptr); + + lldb::ConnectionStatus + BootstrapLookup (const char *port_name, + lldb_private::Error *error_ptr); + + struct PayloadType + { + uint32_t command; + uint32_t data_length; + uint8_t data[32]; + }; + + kern_return_t + Send (const PayloadType &payload); + + kern_return_t + Receive (PayloadType &payload); + + +protected: + mach_port_t m_task; + mach_port_t m_port; + +private: + + + DISALLOW_COPY_AND_ASSIGN (ConnectionMachPort); +}; + +#endif // liblldb_ConnectionMachPort_h_ + +#endif // #if defined(__APPLE__) diff --git a/include/lldb/Core/ConnectionSharedMemory.h b/include/lldb/Core/ConnectionSharedMemory.h new file mode 100644 index 00000000000..0f9cdcb8a92 --- /dev/null +++ b/include/lldb/Core/ConnectionSharedMemory.h @@ -0,0 +1,70 @@ +//===-- ConnectionSharedMemory.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ConnectionSharedMemory_h_ +#define liblldb_ConnectionSharedMemory_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" +#include "lldb/Core/DataBufferMemoryMap.h" + +namespace lldb_private { + +class ConnectionSharedMemory : + public Connection +{ +public: + + ConnectionSharedMemory (); + + virtual + ~ConnectionSharedMemory (); + + virtual bool + IsConnected () const; + + virtual lldb::ConnectionStatus + BytesAvailable (uint32_t timeout_usec, Error *error_ptr); + + virtual lldb::ConnectionStatus + Connect (const char *s, Error *error_ptr); + + virtual lldb::ConnectionStatus + Disconnect (Error *error_ptr); + + virtual size_t + Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + lldb::ConnectionStatus &status, + Error *error_ptr); + + virtual size_t + Write (const void *src, size_t src_len, lldb::ConnectionStatus &status, Error *error_ptr); + + lldb::ConnectionStatus + Open (bool create, const char *name, size_t size, Error *error_ptr); + +protected: + + std::string m_name; + int m_fd; // One buffer that contains all we need + DataBufferMemoryMap m_mmap; +private: + DISALLOW_COPY_AND_ASSIGN (ConnectionSharedMemory); +}; + +} // namespace lldb_private + +#endif // liblldb_ConnectionSharedMemory_h_ diff --git a/include/lldb/Core/ConstString.h b/include/lldb/Core/ConstString.h new file mode 100644 index 00000000000..e692d3b96e5 --- /dev/null +++ b/include/lldb/Core/ConstString.h @@ -0,0 +1,507 @@ +//===-- ConstString.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ConstString_h_ +#define liblldb_ConstString_h_ +#if defined(__cplusplus) + +#include + +#include "lldb/lldb-private.h" +#include "llvm/ADT/StringRef.h" + + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ConstString ConstString.h "lldb/Core/ConstString.h" +/// @brief A uniqued constant string class. +/// +/// Provides an efficient way to store strings as uniqued strings. After +/// the strings are uniqued, finding strings that are equal to one +/// another is very fast as just the pointers need to be compared. It +/// also allows for many common strings from many different sources to +/// be shared to keep the memory footprint low. +/// +/// No reference counting is done on strings that are added to the +/// string pool, once strings are added they are in the string pool for +/// the life of the program. +//---------------------------------------------------------------------- +class ConstString +{ +public: + //------------------------------------------------------------------ + /// Default constructor + /// + /// Initializes the string to an empty string. + //------------------------------------------------------------------ + ConstString (): + m_string (NULL) + { + } + + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Copies the string value in \a rhs into this object. + /// + /// @param[in] rhs + /// Another string object to copy. + //------------------------------------------------------------------ + ConstString (const ConstString& rhs) : + m_string (rhs.m_string) + { + } + + explicit ConstString (const llvm::StringRef &s); + + //------------------------------------------------------------------ + /// Construct with C String value + /// + /// Constructs this object with a C string by looking to see if the + /// C string already exists in the global string pool. If it doesn't + /// exist, it is added to the string pool. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + //------------------------------------------------------------------ + explicit ConstString (const char *cstr); + + //------------------------------------------------------------------ + /// Construct with C String value with max length + /// + /// Constructs this object with a C string with a length. If + /// \a max_cstr_len is greater than the actual length of the string, + /// the string length will be truncated. This allows substrings to + /// be created without the need to NULL terminate the string as it + /// is passed into this function. + /// + /// @param[in] cstr + /// A pointer to the first character in the C string. The C + /// string can be NULL terminated in a buffer that contains + /// more characters than the length of the stirng, or the + /// string can be part of another string and a new substring + /// can be created. + /// + /// @param[in] max_cstr_len + /// The max length of \a cstr. If the string length of \a cstr + /// is less than \a max_cstr_len, then the string will be + /// truncated. If the string length of \a cstr is greater than + /// \a max_cstr_len, then only max_cstr_len bytes will be used + /// from \a cstr. + //------------------------------------------------------------------ + explicit ConstString (const char *cstr, size_t max_cstr_len); + + //------------------------------------------------------------------ + /// Destructor + /// + /// Since constant string values are currently not reference counted, + /// there isn't much to do here. + //------------------------------------------------------------------ + ~ConstString () + { + } + + + //---------------------------------------------------------------------- + /// C string equality binary predicate function object for ConstString + /// objects. + //---------------------------------------------------------------------- + struct StringIsEqual + { + //-------------------------------------------------------------- + /// C equality test. + /// + /// Two C strings are equal when they are contained in ConstString + /// objects when their pointer values are equal to each other. + /// + /// @return + /// Returns \b true if the C string in \a lhs is equal to + /// the C string value in \a rhs, \b false otherwise. + //-------------------------------------------------------------- + bool operator()(const char* lhs, const char* rhs) const + { + return lhs == rhs; + } + }; + + //------------------------------------------------------------------ + /// Convert to bool operator. + /// + /// This allows code to check a ConstString object to see if it + /// contains a valid string using code such as: + /// + /// @code + /// ConstString str(...); + /// if (str) + /// { ... + /// @endcode + /// + /// @return + /// /b True this object contains a valid non-empty C string, \b + /// false otherwise. + //------------------------------------------------------------------ + operator bool() const + { + return m_string && m_string[0]; + } + + //------------------------------------------------------------------ + /// Assignment operator + /// + /// Assigns the string in this object with the value from \a rhs. + /// + /// @param[in] rhs + /// Another string object to copy into this object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ConstString& + operator = (const ConstString& rhs) + { + m_string = rhs.m_string; + return *this; + } + + //------------------------------------------------------------------ + /// Equal to operator + /// + /// Returns true if this string is equal to the string in \a rhs. + /// This operation is very fast as it results in a pointer + /// comparison since all strings are in a uniqued in a global string + /// pool. + /// + /// @param[in] rhs + /// Another string object to compare this object to. + /// + /// @return + /// @li \b true if this object is equal to \a rhs. + /// @li \b false if this object is not equal to \a rhs. + //------------------------------------------------------------------ + bool + operator == (const ConstString& rhs) const + { + // We can do a pointer compare to compare these strings since they + // must come from the same pool in order to be equal. + return m_string == rhs.m_string; + } + + //------------------------------------------------------------------ + /// Not equal to operator + /// + /// Returns true if this string is not equal to the string in \a rhs. + /// This operation is very fast as it results in a pointer + /// comparison since all strings are in a uniqued in a global string + /// pool. + /// + /// @param[in] rhs + /// Another string object to compare this object to. + /// + /// @return + /// @li \b true if this object is not equal to \a rhs. + /// @li \b false if this object is equal to \a rhs. + //------------------------------------------------------------------ + bool + operator != (const ConstString& rhs) const + { + return m_string != rhs.m_string; + } + + bool + operator < (const ConstString& rhs) const; + + //------------------------------------------------------------------ + /// Get the string value as a C string. + /// + /// Get the value of the contained string as a NULL terminated C + /// string value. + /// + /// If \a value_if_empty is NULL, then NULL will be returned. + /// + /// @return + /// Returns \a value_if_empty if the string is empty, otherwise + /// the C string value contained in this object. + //------------------------------------------------------------------ + const char * + AsCString(const char *value_if_empty = NULL) const + { + if (m_string == NULL) + return value_if_empty; + return m_string; + } + + //------------------------------------------------------------------ + /// Get the string value as a llvm::StringRef + /// + /// @return + /// Returns a new llvm::StringRef object filled in with the + /// needed data. + //------------------------------------------------------------------ + llvm::StringRef + GetStringRef () const + { + return llvm::StringRef (m_string, GetLength()); + } + + //------------------------------------------------------------------ + /// Get the string value as a C string. + /// + /// Get the value of the contained string as a NULL terminated C + /// string value. Similar to the ConstString::AsCString() function, + /// yet this function will always return NULL if the string is not + /// valid. So this function is a direct accessor to the string + /// pointer value. + /// + /// @return + /// Returns NULL the string is invalid, otherwise the C string + /// value contained in this object. + //------------------------------------------------------------------ + const char * + GetCString () const + { + return m_string; + } + + + //------------------------------------------------------------------ + /// Get the length in bytes of string value. + /// + /// The string pool stores the length of the string, so we can avoid + /// calling strlen() on the pointer value with this function. + /// + /// @return + /// Returns the number of bytes that this string occupies in + /// memory, not including the NULL termination byte. + //------------------------------------------------------------------ + size_t + GetLength () const; + + //------------------------------------------------------------------ + /// Clear this object's state. + /// + /// Clear any contained string and reset the value to the an empty + /// string value. + //------------------------------------------------------------------ + void + Clear () + { + m_string = NULL; + } + + //------------------------------------------------------------------ + /// Compare two string objects. + /// + /// Compares the C string values contained in \a lhs and \a rhs and + /// returns an integer result. + /// + /// NOTE: only call this function when you want a true string + /// comparision. If you want string equality use the, use the == + /// operator as it is much more efficient. Also if you want string + /// inequality, use the != operator for the same reasons. + /// + /// @param[in] lhs + /// The Left Hand Side const ConstString object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const ConstString object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const ConstString& lhs, const ConstString& rhs); + + //------------------------------------------------------------------ + /// Dump the object description to a stream. + /// + /// Dump the string value to the stream \a s. If the contained string + /// is empty, print \a value_if_empty to the stream instead. If + /// \a value_if_empty is NULL, then nothing will be dumped to the + /// stream. + /// + /// @param[in] s + /// The stream that will be used to dump the object description. + /// + /// @param[in] value_if_empty + /// The value to dump if the string is empty. If NULL, nothing + /// will be output to the stream. + //------------------------------------------------------------------ + void + Dump (Stream *s, const char *value_if_empty = NULL) const; + + //------------------------------------------------------------------ + /// Dump the object debug description to a stream. + /// + /// @param[in] s + /// The stream that will be used to dump the object description. + //------------------------------------------------------------------ + void + DumpDebug (Stream *s) const; + + //------------------------------------------------------------------ + /// Test for empty string. + /// + /// @return + /// @li \b true if the contained string is empty. + /// @li \b false if the contained string is not empty. + //------------------------------------------------------------------ + bool + IsEmpty () const + { + return m_string == NULL || m_string[0] == '\0'; + } + + //------------------------------------------------------------------ + /// Set the C string value. + /// + /// Set the string value in the object by uniquing the \a cstr + /// string value in our global string pool. + /// + /// If the C string already exists in the global string pool, it + /// finds the current entry and returns the existing value. If it + /// doesn't exist, it is added to the string pool. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + //------------------------------------------------------------------ + void + SetCString (const char *cstr); + + void + SetString (const llvm::StringRef &s); + + //------------------------------------------------------------------ + /// Set the C string value and its mangled counterpart. + /// + /// Object files and debug sybmols often use mangled string to + /// represent the linkage name for a symbol, function or global. + /// The string pool can efficiently store these values and their + /// counterparts so when we run into another instance of a mangled + /// name, we can avoid calling the name demangler over and over on + /// the same strings and then trying to unique them. + /// + /// @param[in] demangled + /// The demangled C string to correlate with the \a mangled + /// name. + /// + /// @param[in] mangled + /// The already uniqued mangled ConstString to correlate the + /// soon to be uniqued version of \a demangled. + //------------------------------------------------------------------ + void + SetCStringWithMangledCounterpart (const char *demangled, + const ConstString &mangled); + + //------------------------------------------------------------------ + /// Retrieve the mangled or demangled counterpart for a mangled + /// or demangled ConstString. + /// + /// Object files and debug sybmols often use mangled string to + /// represent the linkage name for a symbol, function or global. + /// The string pool can efficiently store these values and their + /// counterparts so when we run into another instance of a mangled + /// name, we can avoid calling the name demangler over and over on + /// the same strings and then trying to unique them. + /// + /// @param[in] counterpart + /// A reference to a ConstString object that might get filled in + /// with the demangled/mangled counterpart. + /// + /// @return + /// /b True if \a counterpart was filled in with the counterpart + /// /b false otherwise. + //------------------------------------------------------------------ + bool + GetMangledCounterpart (ConstString &counterpart) const; + + //------------------------------------------------------------------ + /// Set the C string value with length. + /// + /// Set the string value in the object by uniquing \a cstr_len bytes + /// starting at the \a cstr string value in our global string pool. + /// If trim is true, then \a cstr_len indicates a maximum length of + /// the CString and if the actual length of the string is less, then + /// it will be trimmed. + /// + /// If the C string already exists in the global string pool, it + /// finds the current entry and returns the existing value. If it + /// doesn't exist, it is added to the string pool. + /// + /// @param[in] cstr + /// A NULL terminated C string to add to the string pool. + /// + /// @param[in] cstr_len + /// The maximum length of the C string. + //------------------------------------------------------------------ + void + SetCStringWithLength (const char *cstr, size_t cstr_len); + + //------------------------------------------------------------------ + /// Set the C string value with the minimum length between + /// \a fixed_cstr_len and the actual length of the C string. This + /// can be used for data structures that have a fixed length to + /// store a C string where the string might not be NULL terminated + /// if the string takes the entire buffer. + //------------------------------------------------------------------ + void + SetTrimmedCStringWithLength (const char *cstr, size_t fixed_cstr_len); + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Return the size in bytes that this object takes in memory. This + /// returns the size in bytes of this object, which does not include + /// any the shared string values it may refer to. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const + { + return sizeof(ConstString); + } + + + //------------------------------------------------------------------ + /// Get the size in bytes of the current global string pool. + /// + /// Reports the the size in bytes of all shared C string values, + /// containers and any other values as a byte size for the + /// entire string pool. + /// + /// @return + /// The number of bytes that the global string pool occupies + /// in memory. + //------------------------------------------------------------------ + static size_t + StaticMemorySize (); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + const char *m_string; +}; + +//------------------------------------------------------------------ +/// Stream the string value \a str to the stream \a s +//------------------------------------------------------------------ +Stream& operator << (Stream& s, const ConstString& str); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_ConstString_h_ diff --git a/include/lldb/Core/DataBuffer.h b/include/lldb/Core/DataBuffer.h new file mode 100644 index 00000000000..e64245dead3 --- /dev/null +++ b/include/lldb/Core/DataBuffer.h @@ -0,0 +1,94 @@ +//===-- DataBuffer.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataBuffer_h_ +#define liblldb_DataBuffer_h_ +#if defined(__cplusplus) + +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataBuffer DataBuffer.h "lldb/Core/DataBuffer.h" +/// @brief A pure virtual protocol class for abstracted data buffers. +/// +/// DataBuffer is an abtract class that gets packaged into a shared pointer +/// that can use to implement various ways to store data (on the heap, +/// memory mapped, cached inferior memory). It gets used by DataExtractor +/// so many DataExtractor objects can share the same data and sub-ranges +/// of that shared data, and the last object that contains a reference +/// to the shared data will free it. +/// +/// Subclasses can implement as many different constructors or member +/// functions that allow data to be stored in the object's buffer prior +/// to handing the shared data to clients that use these buffers. +/// +/// All subclasses must override all of the pure virtual functions as +/// they are used by clients to access the data. Having a common +/// interface allows different ways of storing data, yet using it in +/// one common way. +/// +/// This class currently expects all data to be available without any +/// extra calls being made, but we can modify it to optionally get +/// data on demand with some extra function calls to load the data +/// before it gets accessed. +//---------------------------------------------------------------------- +class DataBuffer +{ +public: + //------------------------------------------------------------------ + /// Destructor + /// + /// The destructor is virtual as other classes will inherit from + /// this class and be downcast to the DataBuffer pure virtual + /// interface. The virtual destructor ensures that destructing the + /// base class will destruct the class that inherited from it + /// correctly. + //------------------------------------------------------------------ + virtual + ~DataBuffer() + { + } + + //------------------------------------------------------------------ + /// Get a pointer to the data. + /// + /// @return + /// A pointer to the bytes owned by this object, or NULL if the + /// object contains no bytes. + //------------------------------------------------------------------ + virtual uint8_t * + GetBytes () = 0; + + //------------------------------------------------------------------ + /// Get a const pointer to the data. + /// + /// @return + /// A const pointer to the bytes owned by this object, or NULL + /// if the object contains no bytes. + //------------------------------------------------------------------ + virtual const uint8_t * + GetBytes () const = 0; + + //------------------------------------------------------------------ + /// Get the number of bytes in the data buffer. + /// + /// @return + /// The number of bytes this object currently contains. + //------------------------------------------------------------------ + virtual lldb::offset_t + GetByteSize() const = 0; +}; + +} // namespace lldb_private + +#endif /// #if defined(__cplusplus) +#endif /// lldb_DataBuffer_h_ diff --git a/include/lldb/Core/DataBufferHeap.h b/include/lldb/Core/DataBufferHeap.h new file mode 100644 index 00000000000..dac9a28befb --- /dev/null +++ b/include/lldb/Core/DataBufferHeap.h @@ -0,0 +1,139 @@ +//===-- DataBufferHeap.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataBufferHeap_h_ +#define liblldb_DataBufferHeap_h_ +#if defined(__cplusplus) + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataBuffer.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataBufferHeap DataBufferHeap.h "lldb/Core/DataBufferHeap.h" +/// @brief A subclass of DataBuffer that stores a data buffer on the heap. +/// +/// This class keeps its data in a heap based buffer that is owned by +/// the object. This class is best used to store chunks of data that +/// are created or read from sources that can't intelligently and lazily +/// fault new data pages in. Large amounts of data that comes from files +/// should probably use the DataBufferMemoryMap class. +//---------------------------------------------------------------------- +class DataBufferHeap : public DataBuffer +{ +public: + //------------------------------------------------------------------ + /// Default constructor + /// + /// Initializes the heap based buffer with no bytes. + //------------------------------------------------------------------ + DataBufferHeap (); + + //------------------------------------------------------------------ + /// Construct with size \a n and fill with \a ch. + /// + /// Initialize this class with \a n bytes and fills the buffer with + /// \a ch. + /// + /// @param[in] n + /// The number of bytes that heap based buffer should contain. + /// + /// @param[in] ch + /// The character to use when filling the buffer initially. + //------------------------------------------------------------------ + DataBufferHeap (lldb::offset_t n, uint8_t ch); + + //------------------------------------------------------------------ + /// Construct by making a copy of \a src_len bytes from \a src. + /// + /// @param[in] src + /// A pointer to the data to copy. + /// + /// @param[in] src_len + /// The number of bytes in \a src to copy. + //------------------------------------------------------------------ + DataBufferHeap (const void *src, lldb::offset_t src_len); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Virtual destructor since this class inherits from a pure virtual + /// base class #DataBuffer. + //------------------------------------------------------------------ + virtual + ~DataBufferHeap(); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() + //------------------------------------------------------------------ + virtual uint8_t * + GetBytes (); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() const + //------------------------------------------------------------------ + virtual const uint8_t * + GetBytes () const; + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetByteSize() const + //------------------------------------------------------------------ + virtual lldb::offset_t + GetByteSize () const; + + //------------------------------------------------------------------ + /// Set the number of bytes in the data buffer. + /// + /// Sets the number of bytes that this object should be able to + /// contain. This can be used prior to copying data into the buffer. + /// + /// @param[in] byte_size + /// The new size in bytes that this data buffer should attempt + /// to resize itself to. + /// + /// @return + /// The size in bytes after that this heap buffer was + /// successfully resized to. + //------------------------------------------------------------------ + lldb::offset_t + SetByteSize (lldb::offset_t byte_size); + + //------------------------------------------------------------------ + /// Makes a copy of the \a src_len bytes in \a src. + /// + /// Copies the data in \a src into an internal buffer. + /// + /// @param[in] src + /// A pointer to the data to copy. + /// + /// @param[in] src_len + /// The number of bytes in \a src to copy. + //------------------------------------------------------------------ + void + CopyData (const void *src, lldb::offset_t src_len); + + void + Clear(); + +private: + //------------------------------------------------------------------ + // This object uses a std::vector to store its data. This + // takes care of free the data when the object is deleted. + //------------------------------------------------------------------ + typedef std::vector buffer_t; ///< Buffer type + buffer_t m_data; ///< The heap based buffer where data is stored +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DataBufferHeap_h_ diff --git a/include/lldb/Core/DataBufferMemoryMap.h b/include/lldb/Core/DataBufferMemoryMap.h new file mode 100644 index 00000000000..d4a448a5df5 --- /dev/null +++ b/include/lldb/Core/DataBufferMemoryMap.h @@ -0,0 +1,160 @@ +//===-- DataBufferMemoryMap.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataBufferMemoryMap_h_ +#define liblldb_DataBufferMemoryMap_h_ +#if defined(__cplusplus) + + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataBufferMemoryMap DataBufferMemoryMap.h "lldb/Core/DataBufferMemoryMap.h" +/// @brief A subclass of DataBuffer that memory maps data. +/// +/// This class memory maps data and stores any needed data for the +/// memory mapping in its internal state. Memory map requests are not +/// required to have any alignment or size constraints, this class will +/// work around any host OS issues regarding such things. +/// +/// This class is designed to allow pages to be faulted in as needed and +/// works well data from large files that won't be accessed all at once. +//---------------------------------------------------------------------- +class DataBufferMemoryMap : public DataBuffer +{ +public: + //------------------------------------------------------------------ + /// Default Constructor + //------------------------------------------------------------------ + DataBufferMemoryMap (); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Virtual destructor since this class inherits from a pure virtual + /// base class #DataBuffer. + //------------------------------------------------------------------ + virtual + ~DataBufferMemoryMap (); + + //------------------------------------------------------------------ + /// Reverts this object to an empty state by unmapping any memory + /// that is currently owned. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() + //------------------------------------------------------------------ + virtual uint8_t * + GetBytes (); + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetBytes() const + //------------------------------------------------------------------ + virtual const uint8_t * + GetBytes () const; + + //------------------------------------------------------------------ + /// @copydoc DataBuffer::GetByteSize() const + //------------------------------------------------------------------ + virtual lldb::offset_t + GetByteSize () const; + + //------------------------------------------------------------------ + /// Error get accessor. + /// + /// @return + /// A const reference to Error object in case memory mapping + /// fails. + //------------------------------------------------------------------ + const Error & + GetError() const; + + //------------------------------------------------------------------ + /// Memory map all or part of a file. + /// + /// Memory map \a length bytes from \a file starting \a offset + /// bytes into the file. If \a length is set to \c SIZE_MAX, + /// then map as many bytes as possible. + /// + /// @param[in] file + /// The file specification from which to map data. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// The number of bytes mapped starting from the \a offset. + //------------------------------------------------------------------ + size_t + MemoryMapFromFileSpec (const FileSpec* file, + lldb::offset_t offset = 0, + lldb::offset_t length = SIZE_MAX, + bool writeable = false); + + //------------------------------------------------------------------ + /// Memory map all or part of a file. + /// + /// Memory map \a length bytes from an opened file descriptor \a fd + /// starting \a offset bytes into the file. If \a length is set to + /// \c SIZE_MAX, then map as many bytes as possible. + /// + /// @param[in] fd + /// The posix file descriptor for an already opened file + /// from which to map data. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// The number of bytes mapped starting from the \a offset. + //------------------------------------------------------------------ + size_t + MemoryMapFromFileDescriptor (int fd, + lldb::offset_t offset, + lldb::offset_t length, + bool write, + bool fd_is_file); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from DataBufferMemoryMap can see and modify these + //------------------------------------------------------------------ + uint8_t * m_mmap_addr; ///< The actual pointer that was returned from \c mmap() + size_t m_mmap_size; ///< The actual number of bytes that were mapped when \c mmap() was called + uint8_t *m_data; ///< The data the user requested somewhere within the memory mapped data. + lldb::offset_t m_size; ///< The size of the data the user got when data was requested + +private: + DISALLOW_COPY_AND_ASSIGN (DataBufferMemoryMap); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DataBufferMemoryMap_h_ diff --git a/include/lldb/Core/DataEncoder.h b/include/lldb/Core/DataEncoder.h new file mode 100644 index 00000000000..658cce0d2b4 --- /dev/null +++ b/include/lldb/Core/DataEncoder.h @@ -0,0 +1,459 @@ +//===-- DataEncoder.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataEncoder_h_ +#define liblldb_DataEncoder_h_ + +#if defined (__cplusplus) + +#include "lldb/lldb-private.h" +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataEncoder DataEncoder.h "lldb/Core/DataEncoder.h" +/// @brief An binary data encoding class. +/// +/// DataEncoder is a class that can encode binary data (swapping if needed) +/// to a data buffer. The data buffer can be caller owned, or can be +/// shared data that can be shared between multiple DataEncoder or +/// DataEncoder instances. +/// +/// @see DataBuffer +//---------------------------------------------------------------------- +class DataEncoder +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all members to a default empty state. + //------------------------------------------------------------------ + DataEncoder (); + + //------------------------------------------------------------------ + /// Construct with a buffer that is owned by the caller. + /// + /// This constructor allows us to use data that is owned by the + /// caller. The data must stay around as long as this object is + /// valid. + /// + /// @param[in] data + /// A pointer to caller owned data. + /// + /// @param[in] data_length + /// The length in bytes of \a data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataEncoder (void* data, uint32_t data_length, lldb::ByteOrder byte_order, uint8_t addr_size); + + //------------------------------------------------------------------ + /// Construct with shared data. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataEncoder (const lldb::DataBufferSP& data_sp, lldb::ByteOrder byte_order, uint8_t addr_size); + + //------------------------------------------------------------------ + /// Destructor + /// + /// If this object contains a valid shared data reference, the + /// reference count on the data will be decremented, and if zero, + /// the data will be freed. + //------------------------------------------------------------------ + ~DataEncoder (); + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state, and + /// release any references to shared data that this object may + /// contain. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Get the current address size. + /// + /// Return the size in bytes of any address values this object will + /// extract. + /// + /// @return + /// The size in bytes of address values that will be extracted. + //------------------------------------------------------------------ + uint8_t + GetAddressByteSize () const + { + return m_addr_size; + } + + + //------------------------------------------------------------------ + /// Get the number of bytes contained in this object. + /// + /// @return + /// The total number of bytes of data this object refers to. + //------------------------------------------------------------------ + size_t + GetByteSize () const + { + return m_end - m_start; + } + + //------------------------------------------------------------------ + /// Get the data end pointer. + /// + /// @return + /// Returns a pointer to the next byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + uint8_t * + GetDataEnd () + { + return m_end; + } + + const uint8_t * + GetDataEnd () const + { + return m_end; + } + + //------------------------------------------------------------------ + /// Get the shared data offset. + /// + /// Get the offset of the first byte of data in the shared data (if + /// any). + /// + /// @return + /// If this object contains shared data, this function returns + /// the offset in bytes into that shared data, zero otherwise. + //------------------------------------------------------------------ + size_t + GetSharedDataOffset () const; + + + //------------------------------------------------------------------ + /// Get the current byte order value. + /// + /// @return + /// The current byte order value from this object's internal + /// state. + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder() const + { + return m_byte_order; + } + + //------------------------------------------------------------------ + /// Get a the data start pointer. + /// + /// @return + /// Returns a pointer to the first byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + uint8_t * + GetDataStart () + { + return m_start; + } + + const uint8_t * + GetDataStart () const + { + return m_start; + } + + //------------------------------------------------------------------ + /// Encode unsigned integer values into the data at \a offset. + /// + /// @param[in] offset + /// The offset within the contained data at which to put the + /// data. + /// + /// @param[in] value + /// The value to encode into the data. + /// + /// @return + /// The next offset in the bytes of this data if the data + /// was successfully encoded, UINT32_MAX if the encoding failed. + //------------------------------------------------------------------ + uint32_t + PutU8 (uint32_t offset, uint8_t value); + + uint32_t + PutU16 (uint32_t offset, uint16_t value); + + uint32_t + PutU32 (uint32_t offset, uint32_t value); + + uint32_t + PutU64 (uint32_t offset, uint64_t value); + + //------------------------------------------------------------------ + /// Encode an unsigned integer of size \a byte_size to \a offset. + /// + /// Encode a single integer value at \a offset and return the offset + /// that follows the newly encoded integer when the data is successfully + /// encoded into the existing data. There must be enough room in the + /// data, else UINT32_MAX will be returned to indicate that encoding + /// failed. + /// + /// @param[in] offset + /// The offset within the contained data at which to put the + /// encoded integer. + /// + /// @param[in] byte_size + /// The size in byte of the integer to encode. + /// + /// @param[in] value + /// The integer value to write. The least significate bytes of + /// the integer value will be written if the size is less than + /// 8 bytes. + /// + /// @return + /// The next offset in the bytes of this data if the integer + /// was successfully encoded, UINT32_MAX if the encoding failed. + //------------------------------------------------------------------ + uint32_t + PutMaxU64 (uint32_t offset, uint32_t byte_size, uint64_t value); + + //------------------------------------------------------------------ + /// Encode an arbitrary number of bytes. + /// + /// @param[in] offset + /// The offset in bytes into the contained data at which to + /// start encoding. + /// + /// @param[int] src + /// The buffer that contains the the bytes to encode. + /// + /// @param[in] src_len + /// The number of bytes to encode. + /// + /// @return + /// The next valid offset within data if the put operation + /// was successful, else UINT32_MAX to indicate the put failed. + //------------------------------------------------------------------ + uint32_t + PutData (uint32_t offset, + const void *src, + uint32_t src_len); + + //------------------------------------------------------------------ + /// Encode an address in the existing buffer at \a offset bytes into + /// the buffer. + /// + /// Encode a single address (honoring the m_addr_size member) to + /// the data and return the next offset where subsequent data would + /// go. + /// pointed to by \a offset_ptr. The size of the extracted address + /// comes from the \a m_addr_size member variable and should be + /// set correctly prior to extracting any address values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The next valid offset within data if the put operation + /// was successful, else UINT32_MAX to indicate the put failed. + //------------------------------------------------------------------ + uint32_t + PutAddress (uint32_t offset, lldb::addr_t addr); + + //------------------------------------------------------------------ + /// Put a C string to \a offset. + /// + /// Encodes a C string into the existing data including the + /// terminating + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// NULL will be returned. + //------------------------------------------------------------------ + uint32_t + PutCString (uint32_t offset_ptr, const char *cstr); + + lldb::DataBufferSP & + GetSharedDataBuffer () + { + return m_data_sp; + } + + //------------------------------------------------------------------ + /// Set the address byte size. + /// + /// Set the size in bytes that will be used when extracting any + /// address and pointer values from data contained in this object. + /// + /// @param[in] addr_size + /// The size in bytes to use when extracting addresses. + //------------------------------------------------------------------ + void + SetAddressByteSize (uint8_t addr_size) + { + m_addr_size = addr_size; + } + + //------------------------------------------------------------------ + /// Set data with a buffer that is caller owned. + /// + /// Use data that is owned by the caller when extracting values. + /// The data must stay around as long as this object, or any object + /// that copies a subset of this object's data, is valid. If \a + /// bytes is NULL, or \a length is zero, this object will contain + /// no data. + /// + /// @param[in] bytes + /// A pointer to caller owned data. + /// + /// @param[in] length + /// The length in bytes of \a bytes. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + uint32_t + SetData (const void *bytes, uint32_t length, lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Adopt a subset of shared data in \a data_sp. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. The byte order + /// and address byte size settings remain the same. If + /// \a offset is not a valid offset in \a data_sp, then no reference + /// to the shared data will be added. If there are not \a length + /// bytes available in \a data starting at \a offset, the length + /// will be truncated to contains as many bytes as possible. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] offset + /// The offset into \a data_sp at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of \a data_sp. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + uint32_t + SetData (const lldb::DataBufferSP& data_sp, uint32_t offset = 0, uint32_t length = UINT32_MAX); + + //------------------------------------------------------------------ + /// Set the byte_order value. + /// + /// Sets the byte order of the data to extract. Extracted values + /// will be swapped if necessary when decoding. + /// + /// @param[in] byte_order + /// The byte order value to use when extracting data. + //------------------------------------------------------------------ + void + SetByteOrder (lldb::ByteOrder byte_order) + { + m_byte_order = byte_order; + } + + + //------------------------------------------------------------------ + /// Test the validity of \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset into the data in this + /// object, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffset (uint32_t offset) const + { + return offset < GetByteSize(); + } + + //------------------------------------------------------------------ + /// Test the availability of \a length bytes of data from \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset and there are \a + /// length bytes available at that offset, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffsetForDataOfSize (uint32_t offset, uint32_t length) const + { + return length <= BytesLeft (offset); + } + + uint32_t + BytesLeft (uint32_t offset) const + { + const uint32_t size = GetByteSize(); + if (size > offset) + return size - offset; + return 0; + } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + uint8_t *m_start; ///< A pointer to the first byte of data. + uint8_t *m_end; ///< A pointer to the byte that is past the end of the data. + lldb::ByteOrder m_byte_order; ///< The byte order of the data we are extracting from. + uint8_t m_addr_size; ///< The address size to use when extracting pointers or addresses + mutable lldb::DataBufferSP m_data_sp; ///< The shared pointer to data that can be shared among multilple instances + +private: + DISALLOW_COPY_AND_ASSIGN (DataEncoder); + +}; + +} // namespace lldb_private + +#endif // #if defined (__cplusplus) +#endif // #ifndef liblldb_DataEncoder_h_ diff --git a/include/lldb/Core/DataExtractor.h b/include/lldb/Core/DataExtractor.h new file mode 100644 index 00000000000..a8593043cb1 --- /dev/null +++ b/include/lldb/Core/DataExtractor.h @@ -0,0 +1,1298 @@ +//===-- DataExtractor.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DataExtractor_h_ +#define liblldb_DataExtractor_h_ +#if defined (__cplusplus) + + +#include "lldb/lldb-private.h" +#include +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DataExtractor DataExtractor.h "lldb/Core/DataExtractor.h" +/// @brief An data extractor class. +/// +/// DataExtractor is a class that can extract data (swapping if needed) +/// from a data buffer. The data buffer can be caller owned, or can be +/// shared data that can be shared between multiple DataExtractor +/// instances. Multiple DataExtractor objects can share the same data, +/// yet extract values in different address sizes and byte order modes. +/// Each object can have a unique position in the shared data and extract +/// data from different offsets. +/// +/// @see DataBuffer +//---------------------------------------------------------------------- +class DataExtractor +{ +public: + //------------------------------------------------------------------ + /// @typedef DataExtractor::Type + /// @brief Type enumerations used in the dump routines. + /// @see DataExtractor::Dump() + /// @see DataExtractor::DumpRawHexBytes() + //------------------------------------------------------------------ + typedef enum + { + TypeUInt8, ///< Format output as unsigned 8 bit integers + TypeChar, ///< Format output as characters + TypeUInt16, ///< Format output as unsigned 16 bit integers + TypeUInt32, ///< Format output as unsigned 32 bit integers + TypeUInt64, ///< Format output as unsigned 64 bit integers + TypePointer, ///< Format output as pointers + TypeULEB128, ///< Format output as ULEB128 numbers + TypeSLEB128 ///< Format output as SLEB128 numbers + } Type; + + static void + DumpHexBytes (Stream *s, + const void *src, + size_t src_len, + uint32_t bytes_per_line, + lldb::addr_t base_addr); // Pass LLDB_INVALID_ADDRESS to not show address at start of line + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all members to a default empty state. + //------------------------------------------------------------------ + DataExtractor (); + + //------------------------------------------------------------------ + /// Construct with a buffer that is owned by the caller. + /// + /// This constructor allows us to use data that is owned by the + /// caller. The data must stay around as long as this object is + /// valid. + /// + /// @param[in] data + /// A pointer to caller owned data. + /// + /// @param[in] data_length + /// The length in bytes of \a data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataExtractor (const void* data, lldb::offset_t data_length, lldb::ByteOrder byte_order, uint32_t addr_size); + + //------------------------------------------------------------------ + /// Construct with shared data. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @param[in] addr_size + /// A new address byte size value. + //------------------------------------------------------------------ + DataExtractor (const lldb::DataBufferSP& data_sp, lldb::ByteOrder byte_order, uint32_t addr_size); + + //------------------------------------------------------------------ + /// Construct with a subset of \a data. + /// + /// Initialize this object with a subset of the data bytes in \a + /// data. If \a data contains shared data, then a reference to the + /// shared data will be added to ensure the shared data stays around + /// as long as any objects have references to the shared data. The + /// byte order value and the address size settings are copied from \a + /// data. If \a offset is not a valid offset in \a data, then no + /// reference to the shared data will be added. If there are not + /// \a length bytes available in \a data starting at \a offset, + /// the length will be truncated to contain as many bytes as + /// possible. + /// + /// @param[in] data + /// Another DataExtractor object that contains data. + /// + /// @param[in] offset + /// The offset into \a data at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of data. + //------------------------------------------------------------------ + DataExtractor (const DataExtractor& data, lldb::offset_t offset, lldb::offset_t length); + + DataExtractor (const DataExtractor& rhs); + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies all data, byte order and address size settings from \a rhs into + /// this object. If \a rhs contains shared data, a reference to that + /// shared data will be added. + /// + /// @param[in] rhs + /// Another DataExtractor object to copy. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const DataExtractor& + operator= (const DataExtractor& rhs); + + //------------------------------------------------------------------ + /// Destructor + /// + /// If this object contains a valid shared data reference, the + /// reference count on the data will be decremented, and if zero, + /// the data will be freed. + //------------------------------------------------------------------ + ~DataExtractor (); + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state, and + /// release any references to shared data that this object may + /// contain. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Dumps the binary data as \a type objects to stream \a s (or to + /// Log() if \a s is NULL) starting \a offset bytes into the data + /// and stopping after dumping \a length bytes. The offset into the + /// data is displayed at the beginning of each line and can be + /// offset by base address \a base_addr. \a num_per_line objects + /// will be displayed on each line. + /// + /// @param[in] s + /// The stream to dump the output to. If NULL the output will + /// be dumped to Log(). + /// + /// @param[in] offset + /// The offset into the data at which to start dumping. + /// + /// @param[in] length + /// The number of bytes to dump. + /// + /// @param[in] base_addr + /// The base address that gets added to the offset displayed on + /// each line. + /// + /// @param[in] num_per_line + /// The number of \a type objects to display on each line. + /// + /// @param[in] type + /// The type of objects to use when dumping data from this + /// object. See DataExtractor::Type. + /// + /// @param[in] type_format + /// The optional format to use for the \a type objects. If this + /// is NULL, the default format for the \a type will be used. + /// + /// @return + /// The offset at which dumping ended. + //------------------------------------------------------------------ + lldb::offset_t + PutToLog (Log *log, + lldb::offset_t offset, + lldb::offset_t length, + uint64_t base_addr, + uint32_t num_per_line, + Type type, + const char *type_format = NULL) const; + + //------------------------------------------------------------------ + /// Dumps \a item_count objects into the stream \a s. + /// + /// Dumps \a item_count objects using \a item_format, each of which + /// are \a item_byte_size bytes long starting at offset \a offset + /// bytes into the contained data, into the stream \a s. \a + /// num_per_line objects will be dumped on each line before a new + /// line will be output. If \a base_addr is a valid address, then + /// each new line of output will be prededed by the address value + /// plus appropriate offset, and a colon and space. Bitfield values + /// can be dumped by calling this function multiple times with the + /// same start offset, format and size, yet differing \a + /// item_bit_size and \a item_bit_offset values. + /// + /// @param[in] s + /// The stream to dump the output to. This value can not be NULL. + /// + /// @param[in] offset + /// The offset into the data at which to start dumping. + /// + /// @param[in] item_format + /// The format to use when dumping each item. + /// + /// @param[in] item_byte_size + /// The byte size of each item. + /// + /// @param[in] item_count + /// The number of items to dump. + /// + /// @param[in] num_per_line + /// The number of items to display on each line. + /// + /// @param[in] base_addr + /// The base address that gets added to the offset displayed on + /// each line if the value is valid. Is \a base_addr is + /// LLDB_INVALID_ADDRESS then no address values will be prepended + /// to any lines. + /// + /// @param[in] item_bit_size + /// If the value to display is a bitfield, this value should + /// be the number of bits that the bitfield item has within the + /// item's byte size value. This function will need to be called + /// multiple times with identical \a offset and \a item_byte_size + /// values in order to display multiple bitfield values that + /// exist within the same integer value. If the items being + /// displayed are not bitfields, this value should be zero. + /// + /// @param[in] item_bit_offset + /// If the value to display is a bitfield, this value should + /// be the offset in bits, or shift right amount, that the + /// bitfield item occupies within the item's byte size value. + /// This function will need to be called multiple times with + /// identical \a offset and \a item_byte_size values in order + /// to display multiple bitfield values that exist within the + /// same integer value. If the items being displayed are not + /// bitfields, this value should be zero. + /// + /// @return + /// The offset at which dumping ended. + //------------------------------------------------------------------ + lldb::offset_t + Dump (Stream *s, + lldb::offset_t 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, + uint32_t item_bit_offset, + ExecutionContextScope *exe_scope = NULL) const; + + //------------------------------------------------------------------ + /// Dump a UUID value at \a offset. + /// + /// Dump a UUID starting at \a offset bytes into this object's data. + /// If the stream \a s is NULL, the output will be sent to Log(). + /// + /// @param[in] s + /// The stream to dump the output to. If NULL the output will + /// be dumped to Log(). + /// + /// @param[in] offset + /// The offset into the data at which to extract and dump a + /// UUID value. + //------------------------------------------------------------------ + void + DumpUUID (Stream *s, lldb::offset_t offset) const; + + //------------------------------------------------------------------ + /// Extract an arbitrary number of bytes in the specified byte + /// order. + /// + /// Attemps to extract \a length bytes starting at \a offset bytes + /// into this data in the requested byte order (\a dst_byte_order) + /// and place the results in \a dst. \a dst must be at least \a + /// length bytes long. + /// + /// @param[in] offset + /// The offset in bytes into the contained data at which to + /// start extracting. + /// + /// @param[in] length + /// The number of bytes to extract. + /// + /// @param[in] dst_byte_order + /// A byte order of the data that we want when the value in + /// copied to \a dst. + /// + /// @param[out] dst + /// The buffer that will receive the extracted value if there + /// are enough bytes available in the current data. + /// + /// @return + /// The number of bytes that were extracted which will be \a + /// length when the value is successfully extracted, or zero + /// if there aren't enough bytes at the specified offset. + //------------------------------------------------------------------ + size_t + ExtractBytes (lldb::offset_t offset, lldb::offset_t length, lldb::ByteOrder dst_byte_order, void *dst) const; + + //------------------------------------------------------------------ + /// Extract an address from \a *offset_ptr. + /// + /// Extract a single address from the data and update the offset + /// pointed to by \a offset_ptr. The size of the extracted address + /// comes from the \a m_addr_size member variable and should be + /// set correctly prior to extracting any address values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted address value. + //------------------------------------------------------------------ + uint64_t + GetAddress (lldb::offset_t *offset_ptr) const; + + uint64_t + GetAddress_unchecked (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Get the current address size. + /// + /// Return the size in bytes of any address values this object will + /// extract. + /// + /// @return + /// The size in bytes of address values that will be extracted. + //------------------------------------------------------------------ + uint32_t + GetAddressByteSize () const + { + return m_addr_size; + } + + //------------------------------------------------------------------ + /// Get the number of bytes contained in this object. + /// + /// @return + /// The total number of bytes of data this object refers to. + //------------------------------------------------------------------ + uint64_t + GetByteSize () const + { + return m_end - m_start; + } + + //------------------------------------------------------------------ + /// Extract a C string from \a *offset_ptr. + /// + /// Returns a pointer to a C String from the data at the offset + /// pointed to by \a offset_ptr. A variable length NULL terminated C + /// string will be extracted and the \a offset_ptr will be + /// updated with the offset of the byte that follows the NULL + /// terminator byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a offset_ptr is out of bounds, or if the + /// offset plus the length of the C string is out of bounds, + /// NULL will be returned. + //------------------------------------------------------------------ + const char * + GetCStr (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract a C string from \a *offset_ptr with field size \a len. + /// + /// Returns a pointer to a C String from the data at the offset + /// pointed to by \a offset_ptr, with a field length of \a len. + /// A NULL terminated C string will be extracted and the \a offset_ptr + /// will be updated with the offset of the byte that follows the fixed + /// length field. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// A pointer to the C string value in the data. If the offset + /// pointed to by \a 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, NULL will + /// be returned. + const char * + GetCStr (lldb::offset_t *offset_ptr, lldb::offset_t len) const; + + //------------------------------------------------------------------ + /// Extract \a length bytes from \a *offset_ptr. + /// + /// Returns a pointer to a bytes in this object's data at the offset + /// pointed to by \a offset_ptr. If \a length is zero or too large, + /// then the offset pointed to by \a offset_ptr will not be updated + /// and NULL will be returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] length + /// The optional length of a string to extract. If the value is + /// zero, a NULL terminated C string will be extracted. + /// + /// @return + /// A pointer to the bytes in this object's data if the offset + /// and length are valid, or NULL otherwise. + //------------------------------------------------------------------ + const void* + GetData (lldb::offset_t *offset_ptr, lldb::offset_t length) const + { + const uint8_t *ptr = PeekData (*offset_ptr, length); + if (ptr) + *offset_ptr += length; + return ptr; + } + + //------------------------------------------------------------------ + /// Copy \a dst_len bytes from \a *offset_ptr and ensure the copied + /// data is treated as a value that can be swapped to match the + /// specified byte order. + /// + /// For values that are larger than the supported integer sizes, + /// this function can be used to extract data in a specified byte + /// order. It can also be used to copy a smaller integer value from + /// to a larger value. The extra bytes left over will be padded + /// correctly according to the byte order of this object and the + /// \a dst_byte_order. This can be very handy when say copying a + /// partial data value into a register. + /// + /// @param[in] src_offset + /// The offset into this data from which to start copying an + /// endian entity + /// + /// @param[in] src_len + /// The length of the endian data to copy from this object + /// into the \a dst object + /// + /// @param[out] dst + /// The buffer where to place the endian data. The data might + /// need to be byte swapped (and appropriately padded with + /// zeroes if \a src_len != \a dst_len) if \a dst_byte_order + /// does not match the byte order in this object. + /// + /// @param[in] dst_len + /// The length number of bytes that the endian value will + /// occupy is \a dst. + /// + /// @param[in] byte_order + /// The byte order that the endian value should be in the \a dst + /// buffer. + /// + /// @return + /// Returns the number of bytes that were copied, or zero if + /// anything goes wrong. + //------------------------------------------------------------------ + lldb::offset_t + CopyByteOrderedData (lldb::offset_t src_offset, + lldb::offset_t src_len, + void *dst, + lldb::offset_t dst_len, + lldb::ByteOrder dst_byte_order) const; + + //------------------------------------------------------------------ + /// Get the data end pointer. + /// + /// @return + /// Returns a pointer to the next byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + const uint8_t * + GetDataEnd () const + { + return m_end; + } + + //------------------------------------------------------------------ + /// Get the shared data offset. + /// + /// Get the offset of the first byte of data in the shared data (if + /// any). + /// + /// @return + /// If this object contains shared data, this function returns + /// the offset in bytes into that shared data, zero otherwise. + //------------------------------------------------------------------ + size_t + GetSharedDataOffset () const; + + //------------------------------------------------------------------ + /// Get a the data start pointer. + /// + /// @return + /// Returns a pointer to the first byte contained in this + /// object's data, or NULL of there is no data in this object. + //------------------------------------------------------------------ + const uint8_t * + GetDataStart () const + { + return m_start; + } + + + //------------------------------------------------------------------ + /// Extract a float from \a *offset_ptr. + /// + /// Extract a single float value. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The floating value that was extracted, or zero on failure. + //------------------------------------------------------------------ + float + GetFloat (lldb::offset_t *offset_ptr) const; + + double + GetDouble (lldb::offset_t *offset_ptr) const; + + long double + GetLongDouble (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract a GNU encoded pointer value from \a *offset_ptr. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] eh_ptr_enc + /// The GNU pointer encoding type. + /// + /// @param[in] pc_rel_addr + /// The PC relative address to use when the encoding is + /// \c DW_GNU_EH_PE_pcrel. + /// + /// @param[in] text_addr + /// The text (code) relative address to use when the encoding is + /// \c DW_GNU_EH_PE_textrel. + /// + /// @param[in] data_addr + /// The data relative address to use when the encoding is + /// \c DW_GNU_EH_PE_datarel. + /// + /// @return + /// The extracted GNU encoded pointer value. + //------------------------------------------------------------------ + uint64_t + GetGNUEHPointer (lldb::offset_t *offset_ptr, + uint32_t eh_ptr_enc, + lldb::addr_t pc_rel_addr, + lldb::addr_t text_addr, + lldb::addr_t data_addr); + + //------------------------------------------------------------------ + /// Extract an integer of size \a byte_size from \a *offset_ptr. + /// + /// Extract a single integer value and update the offset pointed to + /// by \a offset_ptr. The size of the extracted integer is specified + /// by the \a byte_size argument. \a byte_size should have a value + /// >= 1 and <= 4 since the return value is only 32 bits wide. Any + /// \a byte_size values less than 1 or greater than 4 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @return + /// The integer value that was extracted, or zero on failure. + //------------------------------------------------------------------ + uint32_t + GetMaxU32 (lldb::offset_t *offset_ptr, size_t byte_size) const; + + //------------------------------------------------------------------ + /// Extract an unsigned integer of size \a byte_size from \a + /// *offset_ptr. + /// + /// Extract a single unsigned integer value and update the offset + /// pointed to by \a offset_ptr. The size of the extracted integer + /// is specified by the \a byte_size argument. \a byte_size should + /// have a value greater than or equal to one and less than or equal + /// to eight since the return value is 64 bits wide. Any + /// \a byte_size values less than 1 or greater than 8 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @return + /// The unsigned integer value that was extracted, or zero on + /// failure. + //------------------------------------------------------------------ + uint64_t + GetMaxU64 (lldb::offset_t *offset_ptr, size_t byte_size) const; + + uint64_t + GetMaxU64_unchecked (lldb::offset_t *offset_ptr, size_t byte_size) const; + + //------------------------------------------------------------------ + /// Extract an signed integer of size \a byte_size from \a *offset_ptr. + /// + /// Extract a single signed integer value (sign extending if required) + /// and update the offset pointed to by \a offset_ptr. The size of + /// the extracted integer is specified by the \a byte_size argument. + /// \a byte_size should have a value greater than or equal to one + /// and less than or equal to eight since the return value is 64 + /// bits wide. Any \a byte_size values less than 1 or greater than + /// 8 will result in nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @return + /// The sign extended signed integer value that was extracted, + /// or zero on failure. + //------------------------------------------------------------------ + int64_t + GetMaxS64 (lldb::offset_t *offset_ptr, size_t size) const; + + //------------------------------------------------------------------ + /// Extract an unsigned integer of size \a byte_size from \a + /// *offset_ptr, then extract the bitfield from this value if + /// \a bitfield_bit_size is non-zero. + /// + /// Extract a single unsigned integer value and update the offset + /// pointed to by \a offset_ptr. The size of the extracted integer + /// is specified by the \a byte_size argument. \a byte_size should + /// have a value greater than or equal to one and less than or equal + /// to 8 since the return value is 64 bits wide. Any + /// \a byte_size values less than 1 or greater than 8 will result in + /// nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in byte of the integer to extract. + /// + /// @param[in] bitfield_bit_size + /// The size in bits of the bitfield value to extract, or zero + /// to just extract the entire integer value. + /// + /// @param[in] bitfield_bit_offset + /// The bit offset of the bitfield value in the extracted + /// integer (the number of bits to shift the integer to the + /// right). + /// + /// @return + /// The unsigned bitfield integer value that was extracted, or + /// zero on failure. + //------------------------------------------------------------------ + uint64_t + GetMaxU64Bitfield (lldb::offset_t *offset_ptr, + size_t size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset) const; + + //------------------------------------------------------------------ + /// Extract an signed integer of size \a byte_size from \a + /// *offset_ptr, then extract and signe extend the bitfield from + /// this value if \a bitfield_bit_size is non-zero. + /// + /// Extract a single signed integer value (sign extending if required) + /// and update the offset pointed to by \a offset_ptr. The size of + /// the extracted integer is specified by the \a byte_size argument. + /// \a byte_size should have a value greater than or equal to one + /// and less than or equal to eight since the return value is 64 + /// bits wide. Any \a byte_size values less than 1 or greater than + /// 8 will result in nothing being extracted, and zero being returned. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[in] byte_size + /// The size in bytes of the integer to extract. + /// + /// @param[in] bitfield_bit_size + /// The size in bits of the bitfield value to extract, or zero + /// to just extract the entire integer value. + /// + /// @param[in] bitfield_bit_offset + /// The bit offset of the bitfield value in the extracted + /// integer (the number of bits to shift the integer to the + /// right). + /// + /// @return + /// The signed bitfield integer value that was extracted, or + /// zero on failure. + //------------------------------------------------------------------ + int64_t + GetMaxS64Bitfield (lldb::offset_t *offset_ptr, + size_t size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset) const; + + //------------------------------------------------------------------ + /// Extract an pointer from \a *offset_ptr. + /// + /// Extract a single pointer from the data and update the offset + /// pointed to by \a offset_ptr. The size of the extracted pointer + /// comes from the \a m_addr_size member variable and should be + /// set correctly prior to extracting any pointer values. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted pointer value as a 64 integer. + //------------------------------------------------------------------ + uint64_t + GetPointer (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Get the current byte order value. + /// + /// @return + /// The current byte order value from this object's internal + /// state. + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder() const + { + return m_byte_order; + } + + //------------------------------------------------------------------ + /// Extract a uint8_t value from \a *offset_ptr. + /// + /// Extract a single uint8_t from the binary data at the offset + /// pointed to by \a offset_ptr, and advance the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint8_t value. + //------------------------------------------------------------------ + uint8_t + GetU8 ( lldb::offset_t *offset_ptr) const; + + uint8_t + GetU8_unchecked (lldb::offset_t *offset_ptr) const + { + uint8_t val = m_start[*offset_ptr]; + *offset_ptr += 1; + return val; + } + + uint16_t + GetU16_unchecked (lldb::offset_t *offset_ptr) const; + + uint32_t + GetU32_unchecked (lldb::offset_t *offset_ptr) const; + + uint64_t + GetU64_unchecked (lldb::offset_t *offset_ptr) const; + //------------------------------------------------------------------ + /// Extract \a count uint8_t values from \a *offset_ptr. + /// + /// Extract \a count uint8_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint8_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint8_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU8 (lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a uint16_t value from \a *offset_ptr. + /// + /// Extract a single uint16_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint16_t value. + //------------------------------------------------------------------ + uint16_t + GetU16 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract \a count uint16_t values from \a *offset_ptr. + /// + /// Extract \a count uint16_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint16_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint16_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU16 (lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a uint32_t value from \a *offset_ptr. + /// + /// Extract a single uint32_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint32_t value. + //------------------------------------------------------------------ + uint32_t + GetU32 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract \a count uint32_t values from \a *offset_ptr. + /// + /// Extract \a count uint32_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint32_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint32_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU32 (lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a uint64_t value from \a *offset_ptr. + /// + /// Extract a single uint64_t from the binary data at the offset + /// pointed to by \a offset_ptr, and update the offset on success. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted uint64_t value. + //------------------------------------------------------------------ + uint64_t + GetU64 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract \a count uint64_t values from \a *offset_ptr. + /// + /// Extract \a count uint64_t values from the binary data at the + /// offset pointed to by \a offset_ptr, and advance the offset on + /// success. The extracted values are copied into \a dst. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @param[out] dst + /// A buffer to copy \a count uint64_t values into. \a dst must + /// be large enough to hold all requested data. + /// + /// @param[in] count + /// The number of uint64_t values to extract. + /// + /// @return + /// \a dst if all values were properly extracted and copied, + /// NULL otherise. + //------------------------------------------------------------------ + void * + GetU64 ( lldb::offset_t *offset_ptr, void *dst, uint32_t count) const; + + //------------------------------------------------------------------ + /// Extract a signed LEB128 value from \a *offset_ptr. + /// + /// Extracts an signed LEB128 number from this object's data + /// starting at the offset pointed to by \a offset_ptr. The offset + /// pointed to by \a offset_ptr will be updated with the offset of + /// the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted signed integer value. + //------------------------------------------------------------------ + int64_t + GetSLEB128 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Extract a unsigned LEB128 value from \a *offset_ptr. + /// + /// Extracts an unsigned LEB128 number from this object's data + /// starting at the offset pointed to by \a offset_ptr. The offset + /// pointed to by \a offset_ptr will be updated with the offset of + /// the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + /// The extracted unsigned integer value. + //------------------------------------------------------------------ + uint64_t + GetULEB128 (lldb::offset_t *offset_ptr) const; + + lldb::DataBufferSP & + GetSharedDataBuffer () + { + return m_data_sp; + } + + //------------------------------------------------------------------ + /// Peek at a C string at \a offset. + /// + /// 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 \a offset is verified to be a valid offset. + /// + /// @param[in] offset + /// An offset into the data. + /// + /// @return + /// A non-NULL C string pointer if \a offset is a valid offset, + /// NULL otherwise. + //------------------------------------------------------------------ + const char * + PeekCStr (lldb::offset_t offset) const; + + //------------------------------------------------------------------ + /// Peek at a bytes at \a offset. + /// + /// Returns a pointer to \a length bytes at \a offset as long as + /// there are \a length bytes available starting at \a offset. + /// + /// @return + /// A non-NULL data pointer if \a offset is a valid offset and + /// there are \a length bytes available at that offset, NULL + /// otherwise. + //------------------------------------------------------------------ + const uint8_t* + PeekData (lldb::offset_t offset, lldb::offset_t length) const + { + if (length > 0 && ValidOffsetForDataOfSize(offset, length)) + return m_start + offset; + return NULL; + } + + //------------------------------------------------------------------ + /// Set the address byte size. + /// + /// Set the size in bytes that will be used when extracting any + /// address and pointer values from data contained in this object. + /// + /// @param[in] addr_size + /// The size in bytes to use when extracting addresses. + //------------------------------------------------------------------ + void + SetAddressByteSize (uint32_t addr_size) + { + m_addr_size = addr_size; + } + + //------------------------------------------------------------------ + /// Set data with a buffer that is caller owned. + /// + /// Use data that is owned by the caller when extracting values. + /// The data must stay around as long as this object, or any object + /// that copies a subset of this object's data, is valid. If \a + /// bytes is NULL, or \a length is zero, this object will contain + /// no data. + /// + /// @param[in] bytes + /// A pointer to caller owned data. + /// + /// @param[in] length + /// The length in bytes of \a bytes. + /// + /// @param[in] byte_order + /// A byte order of the data that we are extracting from. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + lldb::offset_t + SetData (const void *bytes, lldb::offset_t length, lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Adopt a subset of \a data. + /// + /// Set this object's data to be a subset of the data bytes in \a + /// data. If \a data contains shared data, then a reference to the + /// shared data will be added to ensure the shared data stays around + /// as long as any objects have references to the shared data. The + /// byte order and the address size settings are copied from \a + /// data. If \a offset is not a valid offset in \a data, then no + /// reference to the shared data will be added. If there are not + /// \a length bytes available in \a data starting at \a offset, + /// the length will be truncated to contains as many bytes as + /// possible. + /// + /// @param[in] data + /// Another DataExtractor object that contains data. + /// + /// @param[in] offset + /// The offset into \a data at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of \a data. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + lldb::offset_t + SetData (const DataExtractor& data, lldb::offset_t offset, lldb::offset_t length); + + //------------------------------------------------------------------ + /// Adopt a subset of shared data in \a data_sp. + /// + /// Copies the data shared pointer which adds a reference to the + /// contained in \a data_sp. The shared data reference is reference + /// counted to ensure the data lives as long as anyone still has a + /// valid shared pointer to the data in \a data_sp. The byte order + /// and address byte size settings remain the same. If + /// \a offset is not a valid offset in \a data_sp, then no reference + /// to the shared data will be added. If there are not \a length + /// bytes available in \a data starting at \a offset, the length + /// will be truncated to contains as many bytes as possible. + /// + /// @param[in] data_sp + /// A shared pointer to data. + /// + /// @param[in] offset + /// The offset into \a data_sp at which the subset starts. + /// + /// @param[in] length + /// The length in bytes of the subset of \a data_sp. + /// + /// @return + /// The number of bytes that this object now contains. + //------------------------------------------------------------------ + lldb::offset_t + SetData (const lldb::DataBufferSP& data_sp, lldb::offset_t offset = 0, lldb::offset_t length = LLDB_INVALID_OFFSET); + + //------------------------------------------------------------------ + /// Set the byte_order value. + /// + /// Sets the byte order of the data to extract. Extracted values + /// will be swapped if necessary when decoding. + /// + /// @param[in] byte_order + /// The byte order value to use when extracting data. + //------------------------------------------------------------------ + void + SetByteOrder (lldb::ByteOrder byte_order) + { + m_byte_order = byte_order; + } + + //------------------------------------------------------------------ + /// Skip an LEB128 number at \a *offset_ptr. + /// + /// Skips a LEB128 number (signed or unsigned) from this object's + /// data starting at the offset pointed to by \a offset_ptr. The + /// offset pointed to by \a offset_ptr will be updated with the + /// offset of the byte following the last extracted byte. + /// + /// @param[in,out] offset_ptr + /// A pointer to an offset within the data that will be advanced + /// by the appropriate number of bytes if the value is extracted + /// correctly. If the offset is out of bounds or there are not + /// enough bytes to extract this value, the offset will be left + /// unmodified. + /// + /// @return + // The number of bytes consumed during the extraction. + //------------------------------------------------------------------ + uint32_t + Skip_LEB128 (lldb::offset_t *offset_ptr) const; + + //------------------------------------------------------------------ + /// Test the validity of \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset into the data in this + /// object, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffset (lldb::offset_t offset) const + { + return offset < GetByteSize(); + } + + //------------------------------------------------------------------ + /// Test the availability of \a length bytes of data from \a offset. + /// + /// @return + /// \b true if \a offset is a valid offset and there are \a + /// length bytes available at that offset, \b false otherwise. + //------------------------------------------------------------------ + bool + ValidOffsetForDataOfSize (lldb::offset_t offset, lldb::offset_t length) const + { + return length <= BytesLeft (offset); + } + + size_t + Copy (DataExtractor& dest_data) const; + + bool + Append (DataExtractor& rhs); + + bool + Append (void* bytes, lldb::offset_t length); + + lldb::offset_t + BytesLeft (lldb::offset_t offset) const + { + const lldb::offset_t size = GetByteSize(); + if (size > offset) + return size - offset; + return 0; + } + +protected: + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + const uint8_t * m_start; ///< A pointer to the first byte of data. + const uint8_t * m_end; ///< A pointer to the byte that is past the end of the data. + lldb::ByteOrder m_byte_order; ///< The byte order of the data we are extracting from. + uint32_t m_addr_size; ///< The address size to use when extracting pointers or addresses + mutable lldb::DataBufferSP m_data_sp; ///< The shared pointer to data that can be shared among multilple instances +}; + +} // namespace lldb_private + +#endif // #if defined (__cplusplus) +#endif // #ifndef liblldb_DataExtractor_h_ diff --git a/include/lldb/Core/Debugger.h b/include/lldb/Core/Debugger.h new file mode 100644 index 00000000000..bed93fe0252 --- /dev/null +++ b/include/lldb/Core/Debugger.h @@ -0,0 +1,397 @@ +//===-- Debugger.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Debugger_h_ +#define liblldb_Debugger_h_ +#if defined(__cplusplus) + + +#include +#include + +#include + +#include "lldb/lldb-public.h" + +#include "lldb/API/SBDefines.h" + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/InputReaderStack.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/Terminal.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/TargetList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Debugger Debugger.h "lldb/Core/Debugger.h" +/// @brief A class to manage flag bits. +/// +/// Provides a global root objects for the debugger core. +//---------------------------------------------------------------------- + + +class Debugger : + public std::enable_shared_from_this, + public UserID, + public Properties, + public BroadcasterManager +{ +friend class SourceManager; // For GetSourceFileCache. + +public: + + static lldb::DebuggerSP + CreateInstance (lldb::LogOutputCallback log_callback = NULL, void *baton = NULL); + + static lldb::TargetSP + FindTargetWithProcessID (lldb::pid_t pid); + + static lldb::TargetSP + FindTargetWithProcess (Process *process); + + static void + Initialize (); + + static void + Terminate (); + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + + static void + Destroy (lldb::DebuggerSP &debugger_sp); + + virtual + ~Debugger (); + + void Clear(); + + bool + GetAsyncExecution (); + + void + SetAsyncExecution (bool async); + + File & + GetInputFile () + { + return m_input_file.GetFile(); + } + + File & + GetOutputFile () + { + return m_output_file.GetFile(); + } + + File & + GetErrorFile () + { + return m_error_file.GetFile(); + } + + void + SetInputFileHandle (FILE *fh, bool tranfer_ownership); + + void + SetOutputFileHandle (FILE *fh, bool tranfer_ownership); + + void + SetErrorFileHandle (FILE *fh, bool tranfer_ownership); + + void + SaveInputTerminalState(); + + void + RestoreInputTerminalState(); + + Stream& + GetOutputStream () + { + return m_output_file; + } + + Stream& + GetErrorStream () + { + return m_error_file; + } + + lldb::StreamSP + GetAsyncOutputStream (); + + lldb::StreamSP + GetAsyncErrorStream (); + + CommandInterpreter & + GetCommandInterpreter () + { + assert (m_command_interpreter_ap.get()); + return *m_command_interpreter_ap; + } + + Listener & + GetListener () + { + return m_listener; + } + + // This returns the Debugger's scratch source manager. It won't be able to look up files in debug + // information, but it can look up files by absolute path and display them to you. + // To get the target's source manager, call GetSourceManager on the target instead. + SourceManager & + GetSourceManager (); + +public: + + lldb::TargetSP + GetSelectedTarget () + { + return m_target_list.GetSelectedTarget (); + } + + ExecutionContext + GetSelectedExecutionContext(); + //------------------------------------------------------------------ + /// Get accessor for the target list. + /// + /// The target list is part of the global debugger object. This + /// the single debugger shared instance to control where targets + /// get created and to allow for tracking and searching for targets + /// based on certain criteria. + /// + /// @return + /// A global shared target list. + //------------------------------------------------------------------ + TargetList & + GetTargetList () + { + return m_target_list; + } + + PlatformList & + GetPlatformList () + { + return m_platform_list; + } + + void + DispatchInputInterrupt (); + + void + DispatchInputEndOfFile (); + + void + DispatchInput (const char *bytes, size_t bytes_len); + + void + WriteToDefaultReader (const char *bytes, size_t bytes_len); + + void + PushInputReader (const lldb::InputReaderSP& reader_sp); + + bool + PopInputReader (const lldb::InputReaderSP& reader_sp); + + void + NotifyTopInputReader (lldb::InputReaderAction notification); + + bool + InputReaderIsTopReader (const lldb::InputReaderSP& reader_sp); + + static lldb::DebuggerSP + FindDebuggerWithID (lldb::user_id_t id); + + static lldb::DebuggerSP + FindDebuggerWithInstanceName (const ConstString &instance_name); + + static size_t + GetNumDebuggers(); + + static lldb::DebuggerSP + GetDebuggerAtIndex (size_t index); + + static bool + FormatPrompt (const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + ValueObject* valobj = NULL); + + + void + CleanUpInputReaders (); + + static int + TestDebuggerRefCount (); + + bool + GetCloseInputOnEOF () const; + + void + SetCloseInputOnEOF (bool b); + + bool + EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream); + + void + SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton); + + + //---------------------------------------------------------------------- + // Properties Functions + //---------------------------------------------------------------------- + enum StopDisassemblyType + { + eStopDisassemblyTypeNever = 0, + eStopDisassemblyTypeNoSource, + eStopDisassemblyTypeAlways + }; + + virtual Error + SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value); + + bool + GetAutoConfirm () const; + + const char * + GetFrameFormat() const; + + const char * + GetThreadFormat() const; + + lldb::ScriptLanguage + GetScriptLanguage() const; + + bool + SetScriptLanguage (lldb::ScriptLanguage script_lang); + + uint32_t + GetTerminalWidth () const; + + bool + SetTerminalWidth (uint32_t term_width); + + const char * + GetPrompt() const; + + void + SetPrompt(const char *p); + + bool + GetUseExternalEditor () const; + + bool + SetUseExternalEditor (bool use_external_editor_p); + + bool + GetUseColor () const; + + bool + SetUseColor (bool use_color); + + uint32_t + GetStopSourceLineCount (bool before) const; + + StopDisassemblyType + GetStopDisassemblyDisplay () const; + + uint32_t + GetDisassemblyLineCount () const; + + bool + GetNotifyVoid () const; + + + const ConstString & + GetInstanceName() + { + return m_instance_name; + } + + typedef bool (*LLDBCommandPluginInit) (lldb::SBDebugger& debugger); + + bool + LoadPlugin (const FileSpec& spec, Error& error); + +protected: + + static void + DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len); + + lldb::InputReaderSP + GetCurrentInputReader (); + + void + ActivateInputReader (const lldb::InputReaderSP &reader_sp); + + bool + CheckIfTopInputReaderIsDone (); + + SourceManager::SourceFileCache & + GetSourceFileCache () + { + return m_source_file_cache; + } + Communication m_input_comm; + StreamFile m_input_file; + StreamFile m_output_file; + StreamFile m_error_file; + TerminalState m_terminal_state; + TargetList m_target_list; + PlatformList m_platform_list; + Listener m_listener; + std::unique_ptr m_source_manager_ap; // This is a scratch source manager that we return if we have no targets. + SourceManager::SourceFileCache m_source_file_cache; // All the source managers for targets created in this debugger used this shared + // source file cache. + std::unique_ptr m_command_interpreter_ap; + + InputReaderStack m_input_reader_stack; + std::string m_input_reader_data; + typedef std::map LogStreamMap; + LogStreamMap m_log_streams; + lldb::StreamSP m_log_callback_stream_sp; + ConstString m_instance_name; + typedef std::vector LoadedPluginsList; + LoadedPluginsList m_loaded_plugins; + + void + InstanceInitialize (); + +private: + + // Use Debugger::CreateInstance() to get a shared pointer to a new + // debugger object + Debugger (lldb::LogOutputCallback m_log_callback, void *baton); + + DISALLOW_COPY_AND_ASSIGN (Debugger); + +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Debugger_h_ diff --git a/include/lldb/Core/Disassembler.h b/include/lldb/Core/Disassembler.h new file mode 100644 index 00000000000..d6e90071dc5 --- /dev/null +++ b/include/lldb/Core/Disassembler.h @@ -0,0 +1,422 @@ +//===-- Disassembler.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Disassembler_h_ +#define liblldb_Disassembler_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Opcode.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class Instruction +{ +public: + Instruction (const Address &address, + lldb::AddressClass addr_class = lldb::eAddressClassInvalid); + + virtual + ~Instruction(); + + const Address & + GetAddress () const + { + return m_address; + } + + const char * + GetMnemonic (const ExecutionContext* exe_ctx) + { + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + return m_opcode_name.c_str(); + } + const char * + GetOperands (const ExecutionContext* exe_ctx) + { + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + return m_mnemonics.c_str(); + } + + const char * + GetComment (const ExecutionContext* exe_ctx) + { + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + return m_comment.c_str(); + } + + virtual void + CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx) = 0; + + lldb::AddressClass + GetAddressClass (); + + void + SetAddress (const Address &addr) + { + // Invalidate the address class to lazily discover + // it if we need to. + m_address_class = lldb::eAddressClassInvalid; + m_address = addr; + } + + virtual void + Dump (Stream *s, + uint32_t max_opcode_byte_size, + bool show_address, + bool show_bytes, + const ExecutionContext* exe_ctx); + + virtual bool + DoesBranch () = 0; + + virtual size_t + Decode (const Disassembler &disassembler, + const DataExtractor& data, + lldb::offset_t data_offset) = 0; + + virtual void + SetDescription (const char *) {} // May be overridden in sub-classes that have descriptions. + + lldb::OptionValueSP + ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type); + + lldb::OptionValueSP + ReadDictionary (FILE *in_file, Stream *out_stream); + + bool + DumpEmulation (const ArchSpec &arch); + + virtual bool + TestEmulation (Stream *stream, const char *test_file_name); + + bool + Emulate (const ArchSpec &arch, + uint32_t evaluate_options, + void *baton, + EmulateInstruction::ReadMemoryCallback read_mem_callback, + EmulateInstruction::WriteMemoryCallback write_mem_calback, + EmulateInstruction::ReadRegisterCallback read_reg_callback, + EmulateInstruction::WriteRegisterCallback write_reg_callback); + + const Opcode & + GetOpcode () const + { + return m_opcode; + } + + uint32_t + GetData (DataExtractor &data); + +protected: + Address m_address; // The section offset address of this instruction + // We include an address class in the Instruction class to + // allow the instruction specify the eAddressClassCodeAlternateISA + // (currently used for thumb), and also to specify data (eAddressClassData). + // The usual value will be eAddressClassCode, but often when + // disassembling memory, you might run into data. This can + // help us to disassemble appropriately. +private: + lldb::AddressClass m_address_class; // Use GetAddressClass () accessor function! +protected: + Opcode m_opcode; // The opcode for this instruction + std::string m_opcode_name; + std::string m_mnemonics; + std::string m_comment; + bool m_calculated_strings; + + void + CalculateMnemonicOperandsAndCommentIfNeeded (const ExecutionContext* exe_ctx) + { + if (!m_calculated_strings) + { + m_calculated_strings = true; + CalculateMnemonicOperandsAndComment(exe_ctx); + } + } +}; + + +class InstructionList +{ +public: + InstructionList(); + ~InstructionList(); + + size_t + GetSize() const; + + uint32_t + GetMaxOpcocdeByteSize () const; + + lldb::InstructionSP + GetInstructionAtIndex (size_t idx) const; + + uint32_t + GetIndexOfNextBranchInstruction(uint32_t start) const; + + uint32_t + GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target); + + void + Clear(); + + void + Append (lldb::InstructionSP &inst_sp); + + void + Dump (Stream *s, + bool show_address, + bool show_bytes, + const ExecutionContext* exe_ctx); + +private: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_instructions; +}; + +class PseudoInstruction : + public Instruction +{ +public: + + PseudoInstruction (); + + virtual + ~PseudoInstruction (); + + virtual bool + DoesBranch (); + + virtual void + CalculateMnemonicOperandsAndComment (const ExecutionContext* exe_ctx) + { + // TODO: fill this in and put opcode name into Instruction::m_opcode_name, + // mnemonic into Instruction::m_mnemonics, and any comment into + // Instruction::m_comment + } + + virtual size_t + Decode (const Disassembler &disassembler, + const DataExtractor &data, + lldb::offset_t data_offset); + + void + SetOpcode (size_t opcode_size, void *opcode_data); + + virtual void + SetDescription (const char *description); + +protected: + std::string m_description; + + DISALLOW_COPY_AND_ASSIGN (PseudoInstruction); +}; + +class Disassembler : + public std::enable_shared_from_this, + public PluginInterface +{ +public: + + enum + { + eOptionNone = 0u, + eOptionShowBytes = (1u << 0), + eOptionRawOuput = (1u << 1), + eOptionMarkPCSourceLine = (1u << 2), // Mark the source line that contains the current PC (mixed mode only) + eOptionMarkPCAddress = (1u << 3) // Mark the disassembly line the contains the PC + }; + + enum HexImmediateStyle + { + eHexStyleC, + eHexStyleAsm, + }; + + // FindPlugin should be lax about the flavor string (it is too annoying to have various internal uses of the + // disassembler fail because the global flavor string gets set wrong. Instead, if you get a flavor string you + // don't understand, use the default. Folks who care to check can use the FlavorValidForArchSpec method on the + // disassembler they got back. + static lldb::DisassemblerSP + FindPlugin (const ArchSpec &arch, const char *flavor, const char *plugin_name); + + // This version will use the value in the Target settings if flavor is NULL; + static lldb::DisassemblerSP + FindPluginForTarget(const lldb::TargetSP target_sp, const ArchSpec &arch, const char *flavor, const char *plugin_name); + + static lldb::DisassemblerSP + DisassembleRange (const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &disasm_range); + + static lldb::DisassemblerSP + DisassembleBytes (const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const Address &start, + const void *bytes, + size_t length, + uint32_t max_num_instructions, + bool data_from_file); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const AddressRange &range, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const Address &start, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static size_t + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + SymbolContextList &sc_list, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + const ConstString &name, + Module *module, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + static bool + Disassemble (Debugger &debugger, + const ArchSpec &arch, + const char *plugin_name, + const char *flavor, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Disassembler(const ArchSpec &arch, const char *flavor); + virtual ~Disassembler(); + + typedef const char * (*SummaryCallback)(const Instruction& inst, ExecutionContext *exe_context, void *user_data); + + static bool + PrintInstructions (Disassembler *disasm_ptr, + Debugger &debugger, + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + uint32_t num_mixed_context_lines, + uint32_t options, + Stream &strm); + + size_t + ParseInstructions (const ExecutionContext *exe_ctx, + const AddressRange &range, + Stream *error_strm_ptr, + bool prefer_file_cache); + + size_t + ParseInstructions (const ExecutionContext *exe_ctx, + const Address &range, + uint32_t num_instructions, + bool prefer_file_cache); + + virtual size_t + DecodeInstructions (const Address &base_addr, + const DataExtractor& data, + lldb::offset_t data_offset, + size_t num_instructions, + bool append, + bool data_from_file) = 0; + + InstructionList & + GetInstructionList (); + + const InstructionList & + GetInstructionList () const; + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + const char * + GetFlavor () const + { + return m_flavor.c_str(); + } + + virtual bool + FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) = 0; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Disassembler can see and modify these + //------------------------------------------------------------------ + const ArchSpec m_arch; + InstructionList m_instruction_list; + lldb::addr_t m_base_addr; + std::string m_flavor; + +private: + //------------------------------------------------------------------ + // For Disassembler only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Disassembler); +}; + +} // namespace lldb_private + +#endif // liblldb_Disassembler_h_ diff --git a/include/lldb/Core/EmulateInstruction.h b/include/lldb/Core/EmulateInstruction.h new file mode 100644 index 00000000000..08b97e42491 --- /dev/null +++ b/include/lldb/Core/EmulateInstruction.h @@ -0,0 +1,641 @@ +//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_EmulateInstruction_h_ +#define lldb_EmulateInstruction_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/lldb-public.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/Opcode.h" +#include "lldb/Core/RegisterValue.h" + +//---------------------------------------------------------------------- +/// @class EmulateInstruction EmulateInstruction.h "lldb/Core/EmulateInstruction.h" +/// @brief A class that allows emulation of CPU opcodes. +/// +/// This class is a plug-in interface that is accessed through the +/// standard static FindPlugin function call in the EmulateInstruction +/// class. The FindPlugin takes a target triple and returns a new object +/// if there is a plug-in that supports the architecture and OS. Four +/// callbacks and a baton are provided. The four callbacks are read +/// register, write register, read memory and write memory. +/// +/// This class is currently designed for these main use cases: +/// - Auto generation of Call Frame Information (CFI) from assembly code +/// - Predicting single step breakpoint locations +/// - Emulating instructions for breakpoint traps +/// +/// Objects can be asked to read an instruction which will cause a call +/// to the read register callback to get the PC, followed by a read +/// memory call to read the opcode. If ReadInstruction () returns true, +/// then a call to EmulateInstruction::EvaluateInstruction () can be +/// made. At this point the EmulateInstruction subclass will use all of +/// the callbacks to emulate an instruction. +/// +/// Clients that provide the callbacks can either do the read/write +/// registers/memory to actually emulate the instruction on a real or +/// virtual CPU, or watch for the EmulateInstruction::Context which +/// is context for the read/write register/memory which explains why +/// the callback is being called. Examples of a context are: +/// "pushing register 3 onto the stack at offset -12", or "adjusting +/// stack pointer by -16". This extra context allows the generation of +/// CFI information from assembly code without having to actually do +/// the read/write register/memory. +/// +/// Clients must be prepared that not all instructions for an +/// Instruction Set Architecture (ISA) will be emulated. +/// +/// Subclasses at the very least should implement the instructions that +/// save and restore registers onto the stack and adjustment to the stack +/// pointer. By just implementing a few instructions for an ISA that are +/// the typical prologue opcodes, you can then generate CFI using a +/// class that will soon be available. +/// +/// Implementing all of the instructions that affect the PC can then +/// allow single step prediction support. +/// +/// Implementing all of the instructions allows for emulation of opcodes +/// for breakpoint traps and will pave the way for "thread centric" +/// debugging. The current debugging model is "process centric" where +/// all threads must be stopped when any thread is stopped; when +/// hitting software breakpoints we must disable the breakpoint by +/// restoring the original breakpoint opcde, single stepping and +/// restoring the breakpoint trap. If all threads were allowed to run +/// then other threads could miss the breakpoint. +/// +/// This class centralizes the code that usually is done in separate +/// code paths in a debugger (single step prediction, finding save +/// restore locations of registers for unwinding stack frame variables) +/// and emulating the intruction is just a bonus. +//---------------------------------------------------------------------- + +namespace lldb_private { + +class EmulateInstruction : + public PluginInterface +{ +public: + + static EmulateInstruction* + FindPlugin (const ArchSpec &arch, + InstructionType supported_inst_type, + const char *plugin_name); + + enum ContextType + { + eContextInvalid = 0, + // Read an instruciton opcode from memory + eContextReadOpcode, + + // Usually used for writing a register value whose source value is an + // immediate + eContextImmediate, + + // Exclusively used when saving a register to the stack as part of the + // prologue + eContextPushRegisterOnStack, + + // Exclusively used when restoring a register off the stack as part of + // the epilogue + eContextPopRegisterOffStack, + + // Add or subtract a value from the stack + eContextAdjustStackPointer, + + // Adjust the frame pointer for the current frame + eContextSetFramePointer, + + // Add or subtract a value from a base address register (other than SP) + eContextAdjustBaseRegister, + + // Add or subtract a value from the PC or store a value to the PC. + eContextAdjustPC, + + // Used in WriteRegister callbacks to indicate where the + eContextRegisterPlusOffset, + + // Used in WriteMemory callback to indicate where the data came from + eContextRegisterStore, + + eContextRegisterLoad, + + // Used when performing a PC-relative branch where the + eContextRelativeBranchImmediate, + + // Used when performing an absolute branch where the + eContextAbsoluteBranchRegister, + + // Used when performing a supervisor call to an operating system to + // provide a service: + eContextSupervisorCall, + + // Used when performing a MemU operation to read the PC-relative offset + // from an address. + eContextTableBranchReadMemory, + + // Used when random bits are written into a register + eContextWriteRegisterRandomBits, + + // Used when random bits are written to memory + eContextWriteMemoryRandomBits, + + eContextArithmetic, + + eContextAdvancePC, + + eContextReturnFromException + }; + + enum InfoType { + eInfoTypeRegisterPlusOffset, + eInfoTypeRegisterPlusIndirectOffset, + eInfoTypeRegisterToRegisterPlusOffset, + eInfoTypeRegisterToRegisterPlusIndirectOffset, + eInfoTypeRegisterRegisterOperands, + eInfoTypeOffset, + eInfoTypeRegister, + eInfoTypeImmediate, + eInfoTypeImmediateSigned, + eInfoTypeAddress, + eInfoTypeISAAndImmediate, + eInfoTypeISAAndImmediateSigned, + eInfoTypeISA, + eInfoTypeNoArgs + } InfoType; + + struct Context + { + ContextType type; + enum InfoType info_type; + union + { + struct RegisterPlusOffset + { + RegisterInfo reg; // base register + int64_t signed_offset; // signed offset added to base register + } RegisterPlusOffset; + + struct RegisterPlusIndirectOffset + { + RegisterInfo base_reg; // base register number + RegisterInfo offset_reg; // offset register kind + } RegisterPlusIndirectOffset; + + struct RegisterToRegisterPlusOffset + { + RegisterInfo data_reg; // source/target register for data + RegisterInfo base_reg; // base register for address calculation + int64_t offset; // offset for address calculation + } RegisterToRegisterPlusOffset; + + struct RegisterToRegisterPlusIndirectOffset + { + RegisterInfo base_reg; // base register for address calculation + RegisterInfo offset_reg; // offset register for address calculation + RegisterInfo data_reg; // source/target register for data + } RegisterToRegisterPlusIndirectOffset; + + struct RegisterRegisterOperands + { + RegisterInfo operand1; // register containing first operand for binary op + RegisterInfo operand2; // register containing second operand for binary op + } RegisterRegisterOperands; + + int64_t signed_offset; // signed offset by which to adjust self (for registers only) + + RegisterInfo reg; // plain register + + uint64_t unsigned_immediate;// unsigned immediate value + int64_t signed_immediate; // signed immediate value + + lldb::addr_t address; // direct address + + struct ISAAndImmediate + { + uint32_t isa; + uint32_t unsigned_data32; // immdiate data + } ISAAndImmediate; + + struct ISAAndImmediateSigned + { + uint32_t isa; + int32_t signed_data32; // signed immdiate data + } ISAAndImmediateSigned; + + uint32_t isa; + + } info; + + Context () : + type (eContextInvalid), + info_type (eInfoTypeNoArgs) + { + } + + void + SetRegisterPlusOffset (RegisterInfo base_reg, + int64_t signed_offset) + { + info_type = eInfoTypeRegisterPlusOffset; + info.RegisterPlusOffset.reg = base_reg; + info.RegisterPlusOffset.signed_offset = signed_offset; + } + + void + SetRegisterPlusIndirectOffset (RegisterInfo base_reg, + RegisterInfo offset_reg) + { + info_type = eInfoTypeRegisterPlusIndirectOffset; + info.RegisterPlusIndirectOffset.base_reg = base_reg; + info.RegisterPlusIndirectOffset.offset_reg = offset_reg; + } + + void + SetRegisterToRegisterPlusOffset (RegisterInfo data_reg, + RegisterInfo base_reg, + int64_t offset) + { + info_type = eInfoTypeRegisterToRegisterPlusOffset; + info.RegisterToRegisterPlusOffset.data_reg = data_reg; + info.RegisterToRegisterPlusOffset.base_reg = base_reg; + info.RegisterToRegisterPlusOffset.offset = offset; + } + + void + SetRegisterToRegisterPlusIndirectOffset (RegisterInfo base_reg, + RegisterInfo offset_reg, + RegisterInfo data_reg) + { + info_type = eInfoTypeRegisterToRegisterPlusIndirectOffset; + info.RegisterToRegisterPlusIndirectOffset.base_reg = base_reg; + info.RegisterToRegisterPlusIndirectOffset.offset_reg = offset_reg; + info.RegisterToRegisterPlusIndirectOffset.data_reg = data_reg; + } + + void + SetRegisterRegisterOperands (RegisterInfo op1_reg, + RegisterInfo op2_reg) + { + info_type = eInfoTypeRegisterRegisterOperands; + info.RegisterRegisterOperands.operand1 = op1_reg; + info.RegisterRegisterOperands.operand2 = op2_reg; + } + + void + SetOffset (int64_t signed_offset) + { + info_type = eInfoTypeOffset; + info.signed_offset = signed_offset; + } + + void + SetRegister (RegisterInfo reg) + { + info_type = eInfoTypeRegister; + info.reg = reg; + } + + void + SetImmediate (uint64_t immediate) + { + info_type = eInfoTypeImmediate; + info.unsigned_immediate = immediate; + } + + void + SetImmediateSigned (int64_t signed_immediate) + { + info_type = eInfoTypeImmediateSigned; + info.signed_immediate = signed_immediate; + } + + void + SetAddress (lldb::addr_t address) + { + info_type = eInfoTypeAddress; + info.address = address; + } + void + SetISAAndImmediate (uint32_t isa, uint32_t data) + { + info_type = eInfoTypeISAAndImmediate; + info.ISAAndImmediate.isa = isa; + info.ISAAndImmediate.unsigned_data32 = data; + } + + void + SetISAAndImmediateSigned (uint32_t isa, int32_t data) + { + info_type = eInfoTypeISAAndImmediateSigned; + info.ISAAndImmediateSigned.isa = isa; + info.ISAAndImmediateSigned.signed_data32 = data; + } + + void + SetISA (uint32_t isa) + { + info_type = eInfoTypeISA; + info.isa = isa; + } + + void + SetNoArgs () + { + info_type = eInfoTypeNoArgs; + } + + void + Dump (Stream &s, + EmulateInstruction *instruction) const; + + }; + + typedef size_t (*ReadMemoryCallback) (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + typedef size_t (*WriteMemoryCallback) (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + typedef bool (*ReadRegisterCallback) (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value); + + typedef bool (*WriteRegisterCallback) (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value); + + EmulateInstruction (const ArchSpec &arch); + + virtual ~EmulateInstruction() + { + } + //---------------------------------------------------------------------- + // Mandatory overrides + //---------------------------------------------------------------------- + virtual bool + SupportsEmulatingIntructionsOfType (InstructionType inst_type) = 0; + + virtual bool + SetTargetTriple (const ArchSpec &arch) = 0; + + virtual bool + ReadInstruction () = 0; + + virtual bool + EvaluateInstruction (uint32_t evaluate_options) = 0; + + virtual bool + TestEmulation (Stream *out_stream, ArchSpec &arch, OptionValueDictionary *test_data) = 0; + + virtual bool + GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_info) = 0; + + //---------------------------------------------------------------------- + // Optional overrides + //---------------------------------------------------------------------- + virtual bool + SetInstruction (const Opcode &insn_opcode, const Address &inst_addr, Target *target); + + virtual bool + CreateFunctionEntryUnwind (UnwindPlan &unwind_plan); + + static const char * + TranslateRegister (uint32_t reg_kind, uint32_t reg_num, std::string ®_name); + + //---------------------------------------------------------------------- + // RegisterInfo variants + //---------------------------------------------------------------------- + bool + ReadRegister (const RegisterInfo *reg_info, + RegisterValue& reg_value); + + uint64_t + ReadRegisterUnsigned (const RegisterInfo *reg_info, + uint64_t fail_value, + bool *success_ptr); + + bool + WriteRegister (const Context &context, + const RegisterInfo *ref_info, + const RegisterValue& reg_value); + + bool + WriteRegisterUnsigned (const Context &context, + const RegisterInfo *reg_info, + uint64_t reg_value); + + //---------------------------------------------------------------------- + // Register kind and number variants + //---------------------------------------------------------------------- + bool + ReadRegister (uint32_t reg_kind, + uint32_t reg_num, + RegisterValue& reg_value); + + bool + WriteRegister (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + const RegisterValue& reg_value); + + uint64_t + ReadRegisterUnsigned (uint32_t reg_kind, + uint32_t reg_num, + uint64_t fail_value, + bool *success_ptr); + + bool + WriteRegisterUnsigned (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + uint64_t reg_value); + + + size_t + ReadMemory (const Context &context, + lldb::addr_t addr, + void *dst, + size_t dst_len); + + uint64_t + ReadMemoryUnsigned (const Context &context, + lldb::addr_t addr, + size_t byte_size, + uint64_t fail_value, + bool *success_ptr); + + bool + WriteMemory (const Context &context, + lldb::addr_t addr, + const void *src, + size_t src_len); + + bool + WriteMemoryUnsigned (const Context &context, + lldb::addr_t addr, + uint64_t uval, + size_t uval_byte_size); + + uint32_t + GetAddressByteSize () const + { + return m_arch.GetAddressByteSize(); + } + + lldb::ByteOrder + GetByteOrder () const + { + return m_arch.GetByteOrder(); + } + + const Opcode & + GetOpcode () const + { + return m_opcode; + } + + lldb::addr_t + GetAddress () const + { + return m_addr; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + + static size_t + ReadMemoryFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + static size_t + WriteMemoryFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + static bool + ReadRegisterFrame (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value); + + + static bool + WriteRegisterFrame (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value); + + static size_t + ReadMemoryDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + void *dst, + size_t length); + + static size_t + WriteMemoryDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + lldb::addr_t addr, + const void *dst, + size_t length); + + static bool + ReadRegisterDefault (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value); + + + static bool + WriteRegisterDefault (EmulateInstruction *instruction, + void *baton, + const Context &context, + const RegisterInfo *reg_info, + const RegisterValue ®_value); + + void + SetBaton (void *baton); + + void + SetCallbacks (ReadMemoryCallback read_mem_callback, + WriteMemoryCallback write_mem_callback, + ReadRegisterCallback read_reg_callback, + WriteRegisterCallback write_reg_callback); + + void + SetReadMemCallback (ReadMemoryCallback read_mem_callback); + + void + SetWriteMemCallback (WriteMemoryCallback write_mem_callback); + + void + SetReadRegCallback (ReadRegisterCallback read_reg_callback); + + void + SetWriteRegCallback (WriteRegisterCallback write_reg_callback); + + static bool + GetBestRegisterKindAndNumber (const RegisterInfo *reg_info, + uint32_t ®_kind, + uint32_t ®_num); + + static uint32_t + GetInternalRegisterNumber (RegisterContext *reg_ctx, + const RegisterInfo ®_info); + +protected: + ArchSpec m_arch; + void * m_baton; + ReadMemoryCallback m_read_mem_callback; + WriteMemoryCallback m_write_mem_callback; + ReadRegisterCallback m_read_reg_callback; + WriteRegisterCallback m_write_reg_callback; + lldb::addr_t m_addr; + Opcode m_opcode; + + +private: + //------------------------------------------------------------------ + // For EmulateInstruction only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (EmulateInstruction); +}; + +} // namespace lldb_private + +#endif // lldb_EmulateInstruction_h_ diff --git a/include/lldb/Core/Error.h b/include/lldb/Core/Error.h new file mode 100644 index 00000000000..9e45d5f555d --- /dev/null +++ b/include/lldb/Core/Error.h @@ -0,0 +1,312 @@ +//===-- Error.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __DCError_h__ +#define __DCError_h__ +#if defined(__cplusplus) + +#if defined (__APPLE__) +#include +#endif +#include +#include +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class Log; + +//---------------------------------------------------------------------- +/// @class Error Error.h "lldb/Core/Error.h" +/// @brief An error handling class. +/// +/// This class is designed to be able to hold any error code that can be +/// encountered on a given platform. The errors are stored as a value +/// of type Error::ValueType. This value should be large enough to hold +/// any and all errors that the class supports. Each error has an +/// associated type that is of type lldb::ErrorType. New types +/// can be added to support new error types, and architecture specific +/// types can be enabled. In the future we may wish to switch to a +/// registration mechanism where new error types can be registered at +/// runtime instead of a hard coded scheme. +/// +/// All errors in this class also know how to generate a string +/// representation of themselves for printing results and error codes. +/// The string value will be fetched on demand and its string value will +/// be cached until the error is cleared of the value of the error +/// changes. +//---------------------------------------------------------------------- +class Error +{ +public: + //------------------------------------------------------------------ + /// Every error value that this object can contain needs to be able + /// to fit into ValueType. + //------------------------------------------------------------------ + typedef uint32_t ValueType; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize the error object with a generic success value. + /// + /// @param[in] err + /// An error code. + /// + /// @param[in] type + /// The type for \a err. + //------------------------------------------------------------------ + Error (); + + explicit + Error (ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric); + + explicit + Error (const char* err_str); + + Error (const Error &rhs); + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] err + /// An error code. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const Error& + operator = (const Error& rhs); + + + //------------------------------------------------------------------ + /// Assignment operator from a kern_return_t. + /// + /// Sets the type to \c MachKernel and the error code to \a err. + /// + /// @param[in] err + /// A mach error code. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const Error& + operator = (uint32_t err); + + ~Error(); + + //------------------------------------------------------------------ + /// Get the error string associated with the current error. + // + /// Gets the error value as a NULL terminated C string. The error + /// string will be fetched and cached on demand. The error string + /// will be retrieved from a callback that is appropriate for the + /// type of the error and will be cached until the error value is + /// changed or cleared. + /// + /// @return + /// The error as a NULL terminated C string value if the error + /// is valid and is able to be converted to a string value, + /// NULL otherwise. + //------------------------------------------------------------------ + const char * + AsCString (const char *default_error_str = "unknown error") const; + + //------------------------------------------------------------------ + /// Clear the object state. + /// + /// Reverts the state of this object to contain a generic success + /// value and frees any cached error string value. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Test for error condition. + /// + /// @return + /// \b true if this object contains an error, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + Fail () const; + + //------------------------------------------------------------------ + /// Access the error value. + /// + /// @return + /// The error value. + //------------------------------------------------------------------ + ValueType + GetError () const; + + //------------------------------------------------------------------ + /// Access the error type. + /// + /// @return + /// The error type enumeration value. + //------------------------------------------------------------------ + lldb::ErrorType + GetType () const; + + //------------------------------------------------------------------ + /// Log an error to Log(). + /// + /// Log the error given a formatted string \a format. If the this + /// object contains an error code, update the error string to + /// contain the prefix "error: ", followed by the formatted string, + /// followed by the error value and any string that describes the + /// error value. This allows more context to be given to an error + /// string that remains cached in this object. Logging always occurs + /// even when the error code contains a non-error value. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + void + PutToLog (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + //------------------------------------------------------------------ + /// Log an error to Log() if the error value is an error. + /// + /// Log the error given a formatted string \a format only if the + /// error value in this object describes an error condition. If the + /// this object contains an error, update the error string to + /// contain the prefix "error: " followed by the formatted string, + /// followed by the error value and any string that describes the + /// error value. This allows more context to be given to an error + /// string that remains cached in this object. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + void + LogIfError (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + //------------------------------------------------------------------ + /// Set accessor from a kern_return_t. + /// + /// Set accesssor for the error value to \a err and the error type + /// to \c MachKernel. + /// + /// @param[in] err + /// A mach error code. + //------------------------------------------------------------------ + void + SetMachError (uint32_t err); + + //------------------------------------------------------------------ + /// Set accesssor with an error value and type. + /// + /// Set accesssor for the error value to \a err and the error type + /// to \a type. + /// + /// @param[in] err + /// A mach error code. + /// + /// @param[in] type + /// The type for \a err. + //------------------------------------------------------------------ + void + SetError (ValueType err, lldb::ErrorType type); + + //------------------------------------------------------------------ + /// Set the current error to errno. + /// + /// Update the error value to be \c errno and update the type to + /// be \c Error::POSIX. + //------------------------------------------------------------------ + void + SetErrorToErrno (); + + //------------------------------------------------------------------ + /// Set the current error to a generic error. + /// + /// Update the error value to be \c LLDB_GENERIC_ERROR and update the + /// type to be \c Error::Generic. + //------------------------------------------------------------------ + void + SetErrorToGenericError (); + + //------------------------------------------------------------------ + /// Set the current error string to \a err_str. + /// + /// Set accessor for the error string value for a generic errors, + /// or to supply additional details above and beyond the standard + /// error strings that the standard type callbacks typically + /// provide. This allows custom strings 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. + /// + /// @param err_str + /// The new custom error string to copy and cache. + //------------------------------------------------------------------ + void + SetErrorString (const char *err_str); + + //------------------------------------------------------------------ + /// Set the current error string to a formatted error string. + /// + /// @param format + /// A printf style format string + //------------------------------------------------------------------ + int + SetErrorStringWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + int + SetErrorStringWithVarArg (const char *format, va_list args); + + //------------------------------------------------------------------ + /// Test for success condition. + /// + /// Returns true if the error code in this object is considered a + /// successful return value. + /// + /// @return + /// \b true if this object contains an value that describes + /// success (non-erro), \b false otherwise. + //------------------------------------------------------------------ + bool + Success () const; + + //------------------------------------------------------------------ + /// Test for a failure due to a generic interrupt. + /// + /// Returns true if the error code in this object was caused by an interrupt. + /// At present only supports Posix EINTR. + /// + /// @return + /// \b true if this object contains an value that describes + /// failure due to interrupt, \b false otherwise. + //------------------------------------------------------------------ + bool + WasInterrupted() const; + +protected: + //------------------------------------------------------------------ + /// Member variables + //------------------------------------------------------------------ + ValueType m_code; ///< Error code as an integer value. + lldb::ErrorType m_type; ///< The type of the above error code. + mutable std::string m_string; ///< A string representation of the error code. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef __DCError_h__ diff --git a/include/lldb/Core/Event.h b/include/lldb/Core/Event.h new file mode 100644 index 00000000000..1c3eec0359c --- /dev/null +++ b/include/lldb/Core/Event.h @@ -0,0 +1,217 @@ +//===-- Event.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Event_h_ +#define liblldb_Event_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Host/Predicate.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// lldb::EventData +//---------------------------------------------------------------------- +class EventData +{ + friend class Event; + +public: + EventData (); + + virtual + ~EventData(); + + virtual const ConstString & + GetFlavor () const = 0; + + virtual void + Dump (Stream *s) const; + +private: + virtual void + DoOnRemoval (Event *event_ptr) + { + } + + DISALLOW_COPY_AND_ASSIGN (EventData); + +}; + +//---------------------------------------------------------------------- +// lldb::EventDataBytes +//---------------------------------------------------------------------- +class EventDataBytes : public EventData +{ +public: + //------------------------------------------------------------------ + // Constructors + //------------------------------------------------------------------ + EventDataBytes (); + + EventDataBytes (const char *cstr); + + EventDataBytes (const void *src, size_t src_len); + + virtual + ~EventDataBytes(); + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + virtual const ConstString & + GetFlavor () const; + + virtual void + Dump (Stream *s) const; + + const void * + GetBytes() const; + + size_t + GetByteSize() const; + + void + SetBytes (const void *src, size_t src_len); + + void + SwapBytes (std::string &new_bytes); + + void + SetBytesFromCString (const char *cstr); + + //------------------------------------------------------------------ + // Static functions + //------------------------------------------------------------------ + static const EventDataBytes * + GetEventDataFromEvent (const Event *event_ptr); + + static const void * + GetBytesFromEvent (const Event *event_ptr); + + static size_t + GetByteSizeFromEvent (const Event *event_ptr); + + static const ConstString & + GetFlavorString (); + +private: + std::string m_bytes; + + DISALLOW_COPY_AND_ASSIGN (EventDataBytes); + +}; + +//---------------------------------------------------------------------- +// lldb::Event +//---------------------------------------------------------------------- +class Event +{ + friend class Broadcaster; + friend class Listener; + friend class EventData; + +public: + + Event (Broadcaster *broadcaster, uint32_t event_type, EventData *data = NULL); + + Event (uint32_t event_type, EventData *data = NULL); + + ~Event (); + + void + Dump (Stream *s) const; + + EventData * + GetData () + { + return m_data_ap.get(); + } + + const EventData * + GetData () const + { + return m_data_ap.get(); + } + + void + SetData (EventData *new_data) + { + m_data_ap.reset (new_data); + } + + uint32_t + GetType () const + { + return m_type; + } + + void + SetType (uint32_t new_type) + { + m_type = new_type; + } + + Broadcaster * + GetBroadcaster () const + { + return m_broadcaster; + } + + bool + BroadcasterIs (Broadcaster *broadcaster) + { + return broadcaster == m_broadcaster; + } + + void + Clear() + { + m_data_ap.reset(); + } + + +private: + // This is only called by Listener when it pops an event off the queue for + // the listener. It calls the Event Data's DoOnRemoval() method, which is + // virtual and can be overridden by the specific data classes. + + void + DoOnRemoval (); + + // Called by Broadcaster::BroadcastEvent prior to letting all the listeners + // know about it update the contained broadcaster so that events can be + // popped off one queue and re-broadcast to others. + void + SetBroadcaster (Broadcaster *broadcaster) + { + m_broadcaster = broadcaster; + } + + + Broadcaster * m_broadcaster; // The broadcaster that sent this event + uint32_t m_type; // The bit describing this event + std::unique_ptr m_data_ap; // User specific data for this event + + + DISALLOW_COPY_AND_ASSIGN (Event); + Event(); // Disallow default constructor +}; + +} // namespace lldb_private + +#endif // liblldb_Event_h_ diff --git a/include/lldb/Core/FileLineResolver.h b/include/lldb/Core/FileLineResolver.h new file mode 100644 index 00000000000..e1928f1b063 --- /dev/null +++ b/include/lldb/Core/FileLineResolver.h @@ -0,0 +1,81 @@ +//===-- FileLineResolver.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FileLineResolver_h_ +#define liblldb_FileLineResolver_h_ + +// Project includes +#include "lldb/Core/AddressResolver.h" +#include "lldb/Symbol/SymbolContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FileLineResolver FileLineResolver.h "lldb/Core/FileLineResolver.h" +/// @brief This class finds address for source file and line. Optionally, it will look for inlined +/// instances of the file and line specification. +//---------------------------------------------------------------------- + +class FileLineResolver : + public Searcher +{ +public: + FileLineResolver () : + m_file_spec(), + m_line_number(UINT32_MAX), // Set this to zero for all lines in a file + m_sc_list (), + m_inlines (true) + { + } + + FileLineResolver (const FileSpec &resolver, + uint32_t line_no, + bool check_inlines); + + virtual + ~FileLineResolver (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing); + + virtual Searcher::Depth + GetDepth (); + + virtual void + GetDescription (Stream *s); + + const SymbolContextList & + GetFileLineMatches() + { + return m_sc_list; + } + + void + Clear(); + + void + Reset (const FileSpec &file_spec, + uint32_t line, + bool check_inlines); +protected: + FileSpec m_file_spec; // This is the file spec we are looking for. + uint32_t m_line_number; // This is the line number that we are looking for. + SymbolContextList m_sc_list; + bool m_inlines; // This determines whether the resolver looks for inlined functions or not. + +private: + DISALLOW_COPY_AND_ASSIGN(FileLineResolver); +}; + +} // namespace lldb_private + +#endif // liblldb_FileLineResolver_h_ diff --git a/include/lldb/Core/FileSpecList.h b/include/lldb/Core/FileSpecList.h new file mode 100644 index 00000000000..f94bdae83c0 --- /dev/null +++ b/include/lldb/Core/FileSpecList.h @@ -0,0 +1,243 @@ +//===-- FileSpecList.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FileSpecList_h_ +#define liblldb_FileSpecList_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FileSpecList FileSpecList.h "lldb/Core/FileSpecList.h" +/// @brief A file collection class. +/// +/// A class that contains a mutable list of FileSpec objects. +//---------------------------------------------------------------------- +class FileSpecList +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize this object with an empty file list. + //------------------------------------------------------------------ + FileSpecList (); + + //------------------------------------------------------------------ + /// Copy constructor. + /// + /// Initialize this object with a copy of the file list from \a rhs. + /// + /// @param[in] rhs + /// A const reference to another file list object. + //------------------------------------------------------------------ + FileSpecList (const FileSpecList &rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~FileSpecList (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Replace the file list in this object with the file list from + /// \a rhs. + /// + /// @param[in] rhs + /// A file list object to copy. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const FileSpecList& + operator= (const FileSpecList &rhs); + + //------------------------------------------------------------------ + /// Append a FileSpec object to the list. + /// + /// Appends \a file to the end of the file list. + /// + /// @param[in] file + /// A new file to append to this file list. + //------------------------------------------------------------------ + void + Append (const FileSpec &file); + + //------------------------------------------------------------------ + /// Append a FileSpec object if unique. + /// + /// Appends \a file to the end of the file list if it doesn't + /// already exist in the file list. + /// + /// @param[in] file + /// A new file to append to this file list. + /// + /// @return + /// \b true if the file was appended, \b false otherwise. + //------------------------------------------------------------------ + bool + AppendIfUnique (const FileSpec &file); + + //------------------------------------------------------------------ + /// Clears the file list. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Dumps the file list to the supplied stream pointer "s". + /// + /// @param[in] s + /// The stream that will be used to dump the object description. + //------------------------------------------------------------------ + void + Dump (Stream *s, const char *separator_cstr = "\n") const; + + //------------------------------------------------------------------ + /// Find a file index. + /// + /// Find the index of the file in the file spec list that matches + /// \a file starting \a idx entries into the file spec list. + /// + /// @param[in] idx + /// An index into the file list. + /// + /// @param[in] file + /// The file specification to search for. + /// + /// @param[in] full + /// Should FileSpec::Equal be called with "full" true or false. + /// + /// @return + /// The index of the file that matches \a file if it is found, + /// else UINT32_MAX is returned. + //------------------------------------------------------------------ + size_t + FindFileIndex (size_t idx, const FileSpec &file, bool full) const; + + //------------------------------------------------------------------ + /// Get file at index. + /// + /// Gets a file from the file list. If \a idx is not a valid index, + /// an empty FileSpec object will be returned. The file objects + /// that are returned can be tested using + /// FileSpec::operator void*(). + /// + /// @param[in] idx + /// An index into the file list. + /// + /// @return + /// A copy of the FileSpec object at index \a idx. If \a idx + /// is out of range, then an empty FileSpec object will be + /// returned. + //------------------------------------------------------------------ + const FileSpec & + GetFileSpecAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get file specification pointer at index. + /// + /// Gets a file from the file list. The file objects that are + /// returned can be tested using FileSpec::operator void*(). + /// + /// @param[in] idx + /// An index into the file list. + /// + /// @return + /// A pointer to a contained FileSpec object at index \a idx. + /// If \a idx is out of range, then an NULL is returned. + //------------------------------------------------------------------ + const FileSpec * + GetFileSpecPointerAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// 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. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + + bool + IsEmpty() const + { + return m_files.empty(); + } + + //------------------------------------------------------------------ + /// Get the number of files in the file list. + /// + /// @return + /// The number of files in the file spec list. + //------------------------------------------------------------------ + size_t + GetSize () const; + + bool + Insert (size_t idx, const FileSpec &file) + { + if (idx < m_files.size()) + { + m_files.insert(m_files.begin() + idx, file); + return true; + } + else if (idx == m_files.size()) + { + m_files.push_back(file); + return true; + } + return false; + } + + bool + Replace (size_t idx, const FileSpec &file) + { + if (idx < m_files.size()) + { + m_files[idx] = file; + return true; + } + return false; + } + + bool + Remove (size_t idx) + { + if (idx < m_files.size()) + { + m_files.erase(m_files.begin() + idx); + return true; + } + return false; + } + + static size_t GetFilesMatchingPartialPath (const char *path, bool dir_okay, FileSpecList &matches); + +protected: + typedef std::vector collection; ///< The collection type for the file list. + collection m_files; ///< A collection of FileSpec objects. +}; + +} // namespace lldb_private + + +#endif // #if defined(__cplusplus) +#endif // liblldb_FileSpecList_h_ diff --git a/include/lldb/Core/Flags.h b/include/lldb/Core/Flags.h new file mode 100644 index 00000000000..233f098ead2 --- /dev/null +++ b/include/lldb/Core/Flags.h @@ -0,0 +1,253 @@ +//===-- Flags.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Flags_h_ +#define liblldb_Flags_h_ +#if defined(__cplusplus) + + +#include +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Flags Flags.h "lldb/Core/Flags.h" +/// @brief A class to manage flags. +/// +/// The Flags class managed flag bits and allows testing and +/// modification of individual or multiple flag bits. +//---------------------------------------------------------------------- +class Flags +{ +public: + //---------------------------------------------------------------------- + /// The value type for flags is a 32 bit unsigned integer type. + //---------------------------------------------------------------------- + typedef uint32_t ValueType; + + //---------------------------------------------------------------------- + /// Construct with initial flag bit values. + /// + /// Constructs this object with \a mask as the initial value for all + /// of the flags. + /// + /// @param[in] mask + /// The initial value for all flags. + //---------------------------------------------------------------------- + Flags (ValueType flags = 0) : + m_flags (flags) + { + } + + //---------------------------------------------------------------------- + /// Copy constructor. + /// + /// Construct and copy the flags from \a rhs. + /// + /// @param[in] rhs + /// A const Flags object reference to copy. + //---------------------------------------------------------------------- + Flags (const Flags& rhs) : + m_flags(rhs.m_flags) + { + } + + //---------------------------------------------------------------------- + /// Destructor. + //---------------------------------------------------------------------- + ~Flags () + { + } + + //---------------------------------------------------------------------- + /// Get accessor for all flags. + /// + /// @return + /// Returns all of the flags as a Flags::ValueType. + //---------------------------------------------------------------------- + ValueType + Get () const + { + return m_flags; + } + + //---------------------------------------------------------------------- + /// Return the number of flags that can be represented in this + /// object. + /// + /// @return + /// The maximum number bits in this flag object. + //---------------------------------------------------------------------- + size_t + GetBitSize() const + { + return sizeof (ValueType) * 8; + } + + //---------------------------------------------------------------------- + /// Set accessor for all flags. + /// + /// @param[in] flags + /// The bits with which to replace all of the current flags. + //---------------------------------------------------------------------- + void + Reset (ValueType flags) + { + m_flags = flags; + } + + //---------------------------------------------------------------------- + /// Clear one or more flags. + /// + /// @param[in] mask + /// A bitfield containing one or more flags. + /// + /// @return + /// The new flags after clearing all bits from \a mask. + //---------------------------------------------------------------------- + ValueType + Clear (ValueType mask = ~(ValueType)0) + { + m_flags &= ~mask; + return m_flags; + } + + + //---------------------------------------------------------------------- + /// Set one or more flags by logical OR'ing \a mask with the current + /// flags. + /// + /// @param[in] mask + /// A bitfield containing one or more flags. + /// + /// @return + /// The new flags after setting all bits from \a mask. + //---------------------------------------------------------------------- + ValueType + Set (ValueType mask) + { + m_flags |= mask; + return m_flags; + } + + + //---------------------------------------------------------------------- + /// Test if all bits in \a mask are 1 in the current flags + /// + /// @return + /// \b true if all flags in \a mask are 1, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + AllSet (ValueType mask) const + { + return (m_flags & mask) == mask; + } + + //---------------------------------------------------------------------- + /// Test one or more flags. + /// + /// @return + /// \b true if any flags in \a mask are 1, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + AnySet (ValueType mask) const + { + return (m_flags & mask) != 0; + } + + //---------------------------------------------------------------------- + /// Test a single flag bit. + /// + /// @return + /// \b true if \a bit is set, \b false otherwise. + //---------------------------------------------------------------------- + bool + Test (ValueType bit) const + { + return (m_flags & bit) != 0; + } + + //---------------------------------------------------------------------- + /// Test if all bits in \a mask are clear. + /// + /// @return + /// \b true if \b all flags in \a mask are clear, \b false + /// otherwise. + //---------------------------------------------------------------------- + bool + AllClear (ValueType mask) const + { + return (m_flags & mask) == 0; + } + + bool + AnyClear (ValueType mask) const + { + return (m_flags & mask) != mask; + } + + //---------------------------------------------------------------------- + /// Test a single flag bit to see if it is clear (zero). + /// + /// @return + /// \b true if \a bit is 0, \b false otherwise. + //---------------------------------------------------------------------- + bool + IsClear (ValueType bit) const + { + return (m_flags & bit) == 0; + } + + //---------------------------------------------------------------------- + /// Get the number of zero bits in \a m_flags. + /// + /// @return + /// The number of bits that are set to 0 in the current flags. + //---------------------------------------------------------------------- + size_t + ClearCount () const + { + size_t count = 0; + for (ValueType shift = 0; shift < sizeof(ValueType)*8; ++shift) + { + if ((m_flags & (1u << shift)) == 0) + ++count; + } + return count; + } + + //---------------------------------------------------------------------- + /// Get the number of one bits in \a m_flags. + /// + /// @return + /// The number of bits that are set to 1 in the current flags. + //---------------------------------------------------------------------- + size_t + SetCount () const + { + size_t count = 0; + for (ValueType mask = m_flags; mask; mask >>= 1) + { + if (mask & 1u) + ++count; + } + return count; + } + +protected: + ValueType m_flags; ///< The flags. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Flags_h_ diff --git a/include/lldb/Core/History.h b/include/lldb/Core/History.h new file mode 100644 index 00000000000..b3626882f84 --- /dev/null +++ b/include/lldb/Core/History.h @@ -0,0 +1,177 @@ +//===-- History.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_History_h_ +#define lldb_History_h_ + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class HistorySource History.h "lldb/Core/History.h" +/// @brief A class that defines history events. +//---------------------------------------------------------------------- + +class HistorySource +{ +public: + typedef const void * HistoryEvent; + + HistorySource () : + m_mutex (Mutex::eMutexTypeRecursive), + m_events () + { + } + + virtual + ~HistorySource() + { + } + + // Create a new history event. Subclasses should use any data or members + // in the subclass of this class to produce a history event and push it + // onto the end of the history stack. + + virtual HistoryEvent + CreateHistoryEvent () = 0; + + virtual void + DeleteHistoryEvent (HistoryEvent event) = 0; + + virtual void + DumpHistoryEvent (Stream &strm, HistoryEvent event) = 0; + + virtual size_t + GetHistoryEventCount() = 0; + + virtual HistoryEvent + GetHistoryEventAtIndex (uint32_t idx) = 0; + + virtual HistoryEvent + GetCurrentHistoryEvent () = 0; + + // Return 0 when lhs == rhs, 1 if lhs > rhs, or -1 if lhs < rhs. + virtual int + CompareHistoryEvents (const HistoryEvent lhs, + const HistoryEvent rhs) = 0; + + virtual bool + IsCurrentHistoryEvent (const HistoryEvent event) = 0; + +private: + typedef std::stack collection; + + Mutex m_mutex; + collection m_events; + + DISALLOW_COPY_AND_ASSIGN (HistorySource); + +}; + +//---------------------------------------------------------------------- +/// @class HistorySourceUInt History.h "lldb/Core/History.h" +/// @brief A class that defines history events that are represented by +/// unsigned integers. +/// +/// Any history event that is defined by a unique monotonically +/// increasing unsigned integer +//---------------------------------------------------------------------- + +class HistorySourceUInt : public HistorySource +{ + HistorySourceUInt (const char *id_name, uintptr_t start_value = 0u) : + HistorySource(), + m_name (id_name), + m_curr_id (start_value) + { + } + + virtual + ~HistorySourceUInt() + { + } + + // Create a new history event. Subclasses should use any data or members + // in the subclass of this class to produce a history event and push it + // onto the end of the history stack. + + virtual HistoryEvent + CreateHistoryEvent () + { + ++m_curr_id; + return (HistoryEvent)m_curr_id; + } + + virtual void + DeleteHistoryEvent (HistoryEvent event) + { + // Nothing to delete, the event contains the integer + } + + virtual void + DumpHistoryEvent (Stream &strm, HistoryEvent event); + + virtual size_t + GetHistoryEventCount() + { + return m_curr_id; + } + + virtual HistoryEvent + GetHistoryEventAtIndex (uint32_t idx) + { + return (HistoryEvent)((uintptr_t)idx); + } + + virtual HistoryEvent + GetCurrentHistoryEvent () + { + return (HistoryEvent)m_curr_id; + } + + // Return 0 when lhs == rhs, 1 if lhs > rhs, or -1 if lhs < rhs. + virtual int + CompareHistoryEvents (const HistoryEvent lhs, + const HistoryEvent rhs) + { + uintptr_t lhs_uint = (uintptr_t)lhs; + uintptr_t rhs_uint = (uintptr_t)rhs; + if (lhs_uint < rhs_uint) + return -1; + if (lhs_uint > rhs_uint) + return +1; + return 0; + } + + virtual bool + IsCurrentHistoryEvent (const HistoryEvent event) + { + return (uintptr_t)event == m_curr_id; + } + +protected: + std::string m_name; // The name of the history unsigned integer + uintptr_t m_curr_id; // The current value of the history unsigned unteger +}; + + +} // namespace lldb_private + +#endif // lldb_History_h_ diff --git a/include/lldb/Core/IOStreamMacros.h b/include/lldb/Core/IOStreamMacros.h new file mode 100644 index 00000000000..cc4eeb0e050 --- /dev/null +++ b/include/lldb/Core/IOStreamMacros.h @@ -0,0 +1,38 @@ +//===-- IOStreamMacros.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IOStreamMacros_h_ +#define liblldb_IOStreamMacros_h_ +#if defined(__cplusplus) + +#include + +#define RAW_HEXBASE std::setfill('0') << std::hex << std::right +#define HEXBASE '0' << 'x' << RAW_HEXBASE +#define RAWHEX8(x) RAW_HEXBASE << std::setw(2) << ((uint32_t)(x)) +#define RAWHEX16 RAW_HEXBASE << std::setw(4) +#define RAWHEX32 RAW_HEXBASE << std::setw(8) +#define RAWHEX64 RAW_HEXBASE << std::setw(16) +#define HEX8(x) HEXBASE << std::setw(2) << ((uint32_t)(x)) +#define HEX16 HEXBASE << std::setw(4) +#define HEX32 HEXBASE << std::setw(8) +#define HEX64 HEXBASE << std::setw(16) +#define RAW_HEX(x) RAW_HEXBASE << std::setw(sizeof(x)*2) << (x) +#define HEX(x) HEXBASE << std::setw(sizeof(x)*2) << (x) +#define HEX_SIZE(x, sz) HEXBASE << std::setw((sz)) << (x) +#define STRING_WIDTH(w) std::setfill(' ') << std::setw(w) +#define LEFT_STRING_WIDTH(s, w) std::left << std::setfill(' ') << std::setw(w) << (s) << std::right +#define DECIMAL std::dec << std::setfill(' ') +#define DECIMAL_WIDTH(w) DECIMAL << std::setw(w) +//#define FLOAT(n, d) std::setfill(' ') << std::setw((n)+(d)+1) << std::setprecision(d) << std::showpoint << std::fixed +#define INDENT_WITH_SPACES(iword_idx) std::setfill(' ') << std::setw((iword_idx)) << "" +#define INDENT_WITH_TABS(iword_idx) std::setfill('\t') << std::setw((iword_idx)) << "" + +#endif // #if defined(__cplusplus) +#endif // liblldb_IOStreamMacros_h_ diff --git a/include/lldb/Core/InputReader.h b/include/lldb/Core/InputReader.h new file mode 100644 index 00000000000..fa86e9a39e2 --- /dev/null +++ b/include/lldb/Core/InputReader.h @@ -0,0 +1,274 @@ +//===-- InputReader.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InputReader_h_ +#define liblldb_InputReader_h_ + +#include + +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/StringList.h" +#include "lldb/Host/Predicate.h" + + +namespace lldb_private { + +class InputReader +{ +public: + + typedef size_t (*Callback) (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + struct HandlerData + { + InputReader& reader; + const char *bytes; + size_t bytes_len; + void* baton; + + HandlerData(InputReader& r, + const char* b, + size_t l, + void* t) : + reader(r), + bytes(b), + bytes_len(l), + baton(t) + { + } + + lldb::StreamSP + GetOutStream(); + + bool + GetBatchMode(); + }; + + struct InitializationParameters + { + private: + void* m_baton; + lldb::InputReaderGranularity m_token_size; + char* m_end_token; + char* m_prompt; + bool m_echo; + bool m_save_user_input; + public: + InitializationParameters() : + m_baton(NULL), + m_token_size(lldb::eInputReaderGranularityLine), + m_echo(true), + m_save_user_input(false) + { + SetEndToken("DONE"); + SetPrompt("> "); + } + + InitializationParameters& + SetEcho(bool e) + { + m_echo = e; + return *this; + } + + InitializationParameters& + SetSaveUserInput(bool s) + { + m_save_user_input = s; + return *this; + } + + InitializationParameters& + SetBaton(void* b) + { + m_baton = b; + return *this; + } + + InitializationParameters& + SetGranularity(lldb::InputReaderGranularity g) + { + m_token_size = g; + return *this; + } + + InitializationParameters& + SetEndToken(const char* e) + { + m_end_token = new char[strlen(e)+1]; + ::strcpy(m_end_token,e); + return *this; + } + + InitializationParameters& + SetPrompt(const char* p) + { + m_prompt = new char[strlen(p)+1]; + ::strcpy(m_prompt,p); + return *this; + } + + friend class InputReaderEZ; + + }; + + InputReader (Debugger &debugger); + + virtual + ~InputReader (); + + virtual Error + Initialize (Callback callback, + void *baton, + lldb::InputReaderGranularity token_size, + const char *end_token, + const char *prompt, + bool echo); + + virtual Error Initialize(void* baton, + lldb::InputReaderGranularity token_size = lldb::eInputReaderGranularityLine, + const char* end_token = "DONE", + const char *prompt = "> ", + bool echo = true) + { + return Error("unimplemented"); + } + + virtual Error + Initialize(InitializationParameters& params) + { + return Error("unimplemented"); + } + + // to use these handlers instead of the Callback function, you must subclass + // InputReaderEZ, and redefine the handlers for the events you care about + virtual void + ActivateHandler(HandlerData&) {} + + virtual void + DeactivateHandler(HandlerData&) {} + + virtual void + ReactivateHandler(HandlerData&) {} + + virtual void + AsynchronousOutputWrittenHandler(HandlerData&) {} + + virtual void + GotTokenHandler(HandlerData&) {} + + virtual void + InterruptHandler(HandlerData&) {} + + virtual void + EOFHandler(HandlerData&) {} + + virtual void + DoneHandler(HandlerData&) {} + + bool + IsDone () const + { + return m_done; + } + + void + SetIsDone (bool b) + { + m_done = b; + } + + lldb::InputReaderGranularity + GetGranularity () const + { + return m_granularity; + } + + bool + GetEcho () const + { + return m_echo; + } + + StringList& + GetUserInput() + { + return m_user_input; + } + + virtual bool + GetSaveUserInput() + { + return false; + } + + // Subclasses _can_ override this function to get input as it comes in + // without any granularity + virtual size_t + HandleRawBytes (const char *bytes, size_t bytes_len); + + Debugger & + GetDebugger() + { + return m_debugger; + } + + bool + IsActive () const + { + return m_active; + } + + const char * + GetPrompt () const; + + void + RefreshPrompt(); + + // If you want to read from an input reader synchronously, then just initialize the + // reader and then call WaitOnReaderIsDone, which will return when the reader is popped. + void + WaitOnReaderIsDone (); + + static const char * + GranularityAsCString (lldb::InputReaderGranularity granularity); + +protected: + friend class Debugger; + + void + Notify (lldb::InputReaderAction notification); + + Debugger &m_debugger; + Callback m_callback; + void *m_callback_baton; + std::string m_end_token; + std::string m_prompt; + lldb::InputReaderGranularity m_granularity; + bool m_done; + bool m_echo; + bool m_active; + Predicate m_reader_done; + StringList m_user_input; + bool m_save_user_input; + +private: + DISALLOW_COPY_AND_ASSIGN (InputReader); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_InputReader_h_ diff --git a/include/lldb/Core/InputReaderEZ.h b/include/lldb/Core/InputReaderEZ.h new file mode 100644 index 00000000000..85561b6f0a9 --- /dev/null +++ b/include/lldb/Core/InputReaderEZ.h @@ -0,0 +1,87 @@ +//===-- InputReaderEZ.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InputReaderEZ_h_ +#define liblldb_InputReaderEZ_h_ + +#include "lldb/Core/InputReader.h" + +namespace lldb_private { + +class InputReaderEZ : public InputReader +{ + +private: + + static size_t Callback_Impl(void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); +public: + + InputReaderEZ (Debugger &debugger) : + InputReader(debugger) + {} + + virtual + ~InputReaderEZ (); + + using InputReader::Initialize; + virtual Error + Initialize(void* baton, + lldb::InputReaderGranularity token_size = lldb::eInputReaderGranularityLine, + const char* end_token = "DONE", + const char *prompt = "> ", + bool echo = true); + + virtual Error + Initialize(InitializationParameters& params); + + virtual void + ActivateHandler(HandlerData&) {} + + virtual void + DeactivateHandler(HandlerData&) {} + + virtual void + ReactivateHandler(HandlerData&) {} + + virtual void + AsynchronousOutputWrittenHandler(HandlerData&) {} + + virtual void + GotTokenHandler(HandlerData&) {} + + virtual void + InterruptHandler(HandlerData&) {} + + virtual void + EOFHandler(HandlerData&) {} + + virtual void + DoneHandler(HandlerData&) {} + + virtual bool + GetSaveUserInput() + { + return m_save_user_input; + } + +protected: + friend class Debugger; + +private: + DISALLOW_COPY_AND_ASSIGN (InputReaderEZ); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_InputReaderEZ_h_ diff --git a/include/lldb/Core/InputReaderStack.h b/include/lldb/Core/InputReaderStack.h new file mode 100644 index 00000000000..a73b97cad57 --- /dev/null +++ b/include/lldb/Core/InputReaderStack.h @@ -0,0 +1,58 @@ +//===-- InputReaderStack.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_InputReaderStack_h_ +#define liblldb_InputReaderStack_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class InputReaderStack +{ +public: + + InputReaderStack (); + + ~InputReaderStack (); + + size_t + GetSize () const; + + void + Push (const lldb::InputReaderSP& reader_sp); + + bool + IsEmpty () const; + + lldb::InputReaderSP + Top (); + + void + Pop (); + + Mutex & + GetStackMutex (); + +protected: + + std::stack m_input_readers; + mutable Mutex m_input_readers_mutex; + +private: + + DISALLOW_COPY_AND_ASSIGN (InputReaderStack); +}; + +} // namespace lldb_private + +#endif // liblldb_InputReaderStack_h_ diff --git a/include/lldb/Core/Language.h b/include/lldb/Core/Language.h new file mode 100644 index 00000000000..670c6aa695e --- /dev/null +++ b/include/lldb/Core/Language.h @@ -0,0 +1,117 @@ +//===-- Language.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Language_h_ +#define liblldb_Language_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Language Language.h "lldb/Core/Language.h" +/// @brief Encapsulates the programming language for an lldb object. +/// +/// Languages are represented by an enumeration value. +/// +/// The enumeration values used when describing the programming language +/// are the same values as the latest DWARF specification. +//---------------------------------------------------------------------- +class Language +{ +public: + //------------------------------------------------------------------ + /// Construct with optional language enumeration. + //------------------------------------------------------------------ + Language(lldb::LanguageType language = lldb::eLanguageTypeUnknown); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~Language(); + + //------------------------------------------------------------------ + /// Get the language value as a NULL termianted C string. + /// + /// @return + /// The C string representation of the language. The returned + /// string does not need to be freed as it comes from constant + /// strings. NULL can be returned when the language is set to + /// a value that doesn't match of of the lldb::LanguageType + /// enumerations. + //------------------------------------------------------------------ + const char * + AsCString (lldb::DescriptionLevel level = lldb::eDescriptionLevelBrief) const; + + void + Clear(); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Dump the language value to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the language description. + //------------------------------------------------------------------ + void + Dump(Stream *s) const; + + //------------------------------------------------------------------ + /// Get accessor for the language. + /// + /// @return + /// The enumeration value that describes the programming + /// language that an object is associated with. + //------------------------------------------------------------------ + virtual lldb::LanguageType + GetLanguage() const; + + //------------------------------------------------------------------ + /// Set accessor for the language. + /// + /// @param[in] language + /// The new enumeration value that describes the programming + /// language that an object is associated with. + //------------------------------------------------------------------ + void + SetLanguage(lldb::LanguageType language); + + //------------------------------------------------------------------ + /// Set accessor for the language. + /// + /// @param[in] language_cstr + /// The language name as a C string. + //------------------------------------------------------------------ + bool + SetLanguageFromCString(const char *language_cstr); + + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::LanguageType m_language; ///< The programming language enumeration value. + ///< The enumeration values are the same as the + ///< latest DWARF specification. +}; + +//-------------------------------------------------------------- +/// Stream the language enumeration as a string object to a +/// Stream. +//-------------------------------------------------------------- +Stream& operator << (Stream& s, const Language& language); + +} // namespace lldb_private + +#endif // liblldb_Language_h_ diff --git a/include/lldb/Core/Listener.h b/include/lldb/Core/Listener.h new file mode 100644 index 00000000000..a12a65d705d --- /dev/null +++ b/include/lldb/Core/Listener.h @@ -0,0 +1,194 @@ +//===-- Listener.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Select_h_ +#define liblldb_Select_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include +#include + + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/Predicate.h" +#include "lldb/Core/Event.h" + +namespace lldb_private { + +class Listener +{ +public: + typedef bool (*HandleBroadcastCallback) (lldb::EventSP &event_sp, void *baton); + + friend class Broadcaster; + friend class BroadcasterManager; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Listener (const char *name); + + ~Listener (); + + void + AddEvent (lldb::EventSP &event); + + void + Clear (); + + const char * + GetName () + { + return m_name.c_str(); + } + + uint32_t + StartListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec); + + bool + StopListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec); + + uint32_t + StartListeningForEvents (Broadcaster* broadcaster, + uint32_t event_mask); + + uint32_t + StartListeningForEvents (Broadcaster* broadcaster, + uint32_t event_mask, + HandleBroadcastCallback callback, + void *callback_user_data); + + bool + StopListeningForEvents (Broadcaster* broadcaster, + uint32_t event_mask); + + // Returns true if an event was recieved, false if we timed out. + bool + WaitForEvent (const TimeValue *timeout, + lldb::EventSP &event_sp); + + bool + WaitForEventForBroadcaster (const TimeValue *timeout, + Broadcaster *broadcaster, + lldb::EventSP &event_sp); + + bool + WaitForEventForBroadcasterWithType (const TimeValue *timeout, + Broadcaster *broadcaster, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + Event * + PeekAtNextEvent (); + + Event * + PeekAtNextEventForBroadcaster (Broadcaster *broadcaster); + + Event * + PeekAtNextEventForBroadcasterWithType (Broadcaster *broadcaster, + uint32_t event_type_mask); + + bool + GetNextEvent (lldb::EventSP &event_sp); + + bool + GetNextEventForBroadcaster (Broadcaster *broadcaster, + lldb::EventSP &event_sp); + + bool + GetNextEventForBroadcasterWithType (Broadcaster *broadcaster, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + size_t + HandleBroadcastEvent (lldb::EventSP &event_sp); + +private: + + //------------------------------------------------------------------ + // Classes that inherit from Listener can see and modify these + //------------------------------------------------------------------ + struct BroadcasterInfo + { + BroadcasterInfo(uint32_t mask, HandleBroadcastCallback cb = NULL, void *ud = NULL) : + event_mask (mask), + callback (cb), + callback_user_data (ud) + { + } + + uint32_t event_mask; + HandleBroadcastCallback callback; + void *callback_user_data; + }; + + typedef std::multimap broadcaster_collection; + typedef std::list event_collection; + typedef std::vector broadcaster_manager_collection; + + bool + FindNextEventInternal (Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *sources, // NULL for any event + uint32_t num_sources, + uint32_t event_type_mask, + lldb::EventSP &event_sp, + bool remove); + + bool + GetNextEventInternal (Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *sources, // NULL for any event + uint32_t num_sources, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + bool + WaitForEventsInternal (const TimeValue *timeout, + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *sources, // NULL for any event + uint32_t num_sources, + uint32_t event_type_mask, + lldb::EventSP &event_sp); + + std::string m_name; + broadcaster_collection m_broadcasters; + Mutex m_broadcasters_mutex; // Protects m_broadcasters + event_collection m_events; + Mutex m_events_mutex; // Protects m_broadcasters and m_events + Predicate m_cond_wait; + broadcaster_manager_collection m_broadcaster_managers; + + void + BroadcasterWillDestruct (Broadcaster *); + + void + BroadcasterManagerWillDestruct (BroadcasterManager *manager); + + +// broadcaster_collection::iterator +// FindBroadcasterWithMask (Broadcaster *broadcaster, +// uint32_t event_mask, +// bool exact); + + //------------------------------------------------------------------ + // For Listener only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (Listener); +}; + +} // namespace lldb_private + +#endif // liblldb_Select_h_ diff --git a/include/lldb/Core/Log.h b/include/lldb/Core/Log.h new file mode 100644 index 00000000000..ced6f2565d9 --- /dev/null +++ b/include/lldb/Core/Log.h @@ -0,0 +1,236 @@ +//===-- Log.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Log_h_ +#define liblldb_Log_h_ + +// C Includes +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/PluginInterface.h" + +//---------------------------------------------------------------------- +// Logging types +//---------------------------------------------------------------------- +#define LLDB_LOG_FLAG_STDOUT (1u << 0) +#define LLDB_LOG_FLAG_STDERR (1u << 1) +#define LLDB_LOG_FLAG_FATAL (1u << 2) +#define LLDB_LOG_FLAG_ERROR (1u << 3) +#define LLDB_LOG_FLAG_WARNING (1u << 4) +#define LLDB_LOG_FLAG_DEBUG (1u << 5) +#define LLDB_LOG_FLAG_VERBOSE (1u << 6) + +//---------------------------------------------------------------------- +// Logging Options +//---------------------------------------------------------------------- +#define LLDB_LOG_OPTION_THREADSAFE (1u << 0) +#define LLDB_LOG_OPTION_VERBOSE (1u << 1) +#define LLDB_LOG_OPTION_DEBUG (1u << 2) +#define LLDB_LOG_OPTION_PREPEND_SEQUENCE (1u << 3) +#define LLDB_LOG_OPTION_PREPEND_TIMESTAMP (1u << 4) +#define LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD (1u << 5) +#define LLDB_LOG_OPTION_PREPEND_THREAD_NAME (1U << 6) +#define LLDB_LOG_OPTION_BACKTRACE (1U << 7) + +//---------------------------------------------------------------------- +// Logging Functions +//---------------------------------------------------------------------- +namespace lldb_private { + +class Log +{ +public: + + //------------------------------------------------------------------ + // Callback definitions for abstracted plug-in log access. + //------------------------------------------------------------------ + typedef void (*DisableCallback) (const char **categories, Stream *feedback_strm); + typedef Log * (*EnableCallback) (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm); + typedef void (*ListCategoriesCallback) (Stream *strm); + + struct Callbacks + { + DisableCallback disable; + EnableCallback enable; + ListCategoriesCallback list_categories; + }; + + //------------------------------------------------------------------ + // Static accessors for logging channels + //------------------------------------------------------------------ + static void + RegisterLogChannel (const ConstString &channel, + const Log::Callbacks &log_callbacks); + + static bool + UnregisterLogChannel (const ConstString &channel); + + static bool + GetLogChannelCallbacks (const ConstString &channel, + Log::Callbacks &log_callbacks); + + + static void + EnableAllLogChannels (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm); + + static void + DisableAllLogChannels (Stream *feedback_strm); + + static void + ListAllLogChannels (Stream *strm); + + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------------ + // Auto completion + //------------------------------------------------------------------ + static void + AutoCompleteChannelName (const char *channel_name, + StringList &matches); + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + Log (); + + Log (const lldb::StreamSP &stream_sp); + + ~Log (); + + void + PutCString (const char *cstr); + + void + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + VAPrintf (const char *format, va_list args); + + void + PrintfWithFlags( uint32_t flags, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + void + LogIf (uint32_t mask, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); + + void + Debug (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + DebugVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + Error (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + FatalError (int err, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); + + void + Verbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + Warning (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + void + WarningVerbose (const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + Flags & + GetOptions(); + + const Flags & + GetOptions() const; + + Flags & + GetMask(); + + const Flags & + GetMask() const; + + bool + GetVerbose() const; + + bool + GetDebug() const; + + void + SetStream (const lldb::StreamSP &stream_sp) + { + m_stream_sp = stream_sp; + } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::StreamSP m_stream_sp; + Flags m_options; + Flags m_mask_bits; + + void + PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args); + +private: + DISALLOW_COPY_AND_ASSIGN (Log); +}; + + +class LogChannel : public PluginInterface +{ +public: + LogChannel (); + + virtual + ~LogChannel (); + + static lldb::LogChannelSP + FindPlugin (const char *plugin_name); + + // categories is a an array of chars that ends with a NULL element. + virtual void + Disable (const char **categories, Stream *feedback_strm) = 0; + + virtual bool + Enable (lldb::StreamSP &log_stream_sp, + uint32_t log_options, + Stream *feedback_strm, // Feedback stream for argument errors etc + const char **categories) = 0;// The categories to enable within this logging stream, if empty, enable default set + + virtual void + ListCategories (Stream *strm) = 0; + +protected: + std::unique_ptr m_log_ap; + +private: + DISALLOW_COPY_AND_ASSIGN (LogChannel); +}; + + +} // namespace lldb_private + +#endif // liblldb_Log_H_ diff --git a/include/lldb/Core/Mangled.h b/include/lldb/Core/Mangled.h new file mode 100644 index 00000000000..8732dc00270 --- /dev/null +++ b/include/lldb/Core/Mangled.h @@ -0,0 +1,306 @@ +//===-- Mangled.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Mangled_h_ +#define liblldb_Mangled_h_ +#if defined(__cplusplus) + + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Mangled Mangled.h "lldb/Core/Mangled.h" +/// @brief A class that handles mangled names. +/// +/// Designed to handle mangled names. The demangled version of any names +/// will be computed when the demangled name is accessed through the +/// Demangled() acccessor. This class can also tokenize the demangled +/// version of the name for powerful searches. Functions and symbols +/// could make instances of this class for their mangled names. Uniqued +/// string pools are used for the mangled, demangled, and token string +/// values to allow for faster comparisons and for efficient memory use. +//---------------------------------------------------------------------- +class Mangled +{ +public: + + enum NamePreference + { + ePreferMangled, + ePreferDemangled + }; + + //---------------------------------------------------------------------- + /// Default constructor. + /// + /// Initialize with both mangled and demangled names empty. + //---------------------------------------------------------------------- + Mangled (); + + //---------------------------------------------------------------------- + /// Construct with name. + /// + /// Constructor with an optional string and a boolean indicating if it is + /// the mangled version. + /// + /// @param[in] name + /// The already const name to copy into this object. + /// + /// @param[in] is_mangled + /// If \b true then \a name is a mangled name, if \b false then + /// \a name is demangled. + //---------------------------------------------------------------------- + explicit + Mangled (const ConstString &name, bool is_mangled); + + //---------------------------------------------------------------------- + /// Construct with name. + /// + /// Constructor with an optional string and auto-detect if \a name is + /// mangled or not. + /// + /// @param[in] name + /// The already const name to copy into this object. + //---------------------------------------------------------------------- + explicit + Mangled (const ConstString &name); + + //---------------------------------------------------------------------- + /// Destructor + /// + /// Releases its ref counts on the mangled and demangled strings that + /// live in the global string pool. + //---------------------------------------------------------------------- + ~Mangled (); + + //---------------------------------------------------------------------- + /// Convert to pointer operator. + /// + /// This allows code to check a Mangled object to see if it contains + /// a valid mangled name using code such as: + /// + /// @code + /// Mangled mangled(...); + /// if (mangled) + /// { ... + /// @endcode + /// + /// @return + /// A pointer to this object if either the mangled or unmangled + /// name is set, NULL otherwise. + //---------------------------------------------------------------------- + operator + void*() const; + + //---------------------------------------------------------------------- + /// Logical NOT operator. + /// + /// This allows code to check a Mangled object to see if it contains + /// an empty mangled name using code such as: + /// + /// @code + /// Mangled mangled(...); + /// if (!mangled) + /// { ... + /// @endcode + /// + /// @return + /// Returns \b true if the object has an empty mangled and + /// unmangled name, \b false otherwise. + //---------------------------------------------------------------------- + bool + operator!() const; + + //---------------------------------------------------------------------- + /// Clear the mangled and demangled values. + //---------------------------------------------------------------------- + void + Clear (); + + //---------------------------------------------------------------------- + /// Compare the mangled string values + /// + /// Compares the Mangled::GetName() string in \a lhs and \a rhs. + /// + /// @param[in] lhs + /// A const reference to the Left Hand Side object to compare. + /// + /// @param[in] rhs + /// A const reference to the Right Hand Side object to compare. + /// + /// @return + /// @li -1 if \a lhs is less than \a rhs + /// @li 0 if \a lhs is equal to \a rhs + /// @li 1 if \a lhs is greater than \a rhs + //---------------------------------------------------------------------- + static int + Compare (const Mangled& lhs, const Mangled& rhs); + + //---------------------------------------------------------------------- + /// Dump a description of this object to a Stream \a s. + /// + /// Dump a Mangled object to stream \a s. We don't force our + /// demangled name to be computed currently (we don't use the accessor). + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //---------------------------------------------------------------------- + void + Dump (Stream *s) const; + + //---------------------------------------------------------------------- + /// Dump a debug description of this object to a Stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //---------------------------------------------------------------------- + void + DumpDebug (Stream *s) const; + + //---------------------------------------------------------------------- + /// Demangled name get accessor. + /// + /// @return + /// A const reference to the demangled name string object. + //---------------------------------------------------------------------- + const ConstString& + GetDemangledName () const; + + void + SetDemangledName (const ConstString &name) + { + m_demangled = name; + } + + void + SetMangledName (const ConstString &name) + { + m_mangled = name; + } + + //---------------------------------------------------------------------- + /// Mangled name get accessor. + /// + /// @return + /// A reference to the mangled name string object. + //---------------------------------------------------------------------- + ConstString& + GetMangledName () + { + return m_mangled; + } + + //---------------------------------------------------------------------- + /// Mangled name get accessor. + /// + /// @return + /// A const reference to the mangled name string object. + //---------------------------------------------------------------------- + const ConstString& + GetMangledName () const + { + return m_mangled; + } + + //---------------------------------------------------------------------- + /// Best name get accessor. + /// + /// @param[in] preference + /// Which name would you prefer to get? + /// + /// @return + /// A const reference to the the preferred name string object if this + /// object has a valid name of that kind, else a const reference to the + /// other name is returned. + //---------------------------------------------------------------------- + const ConstString& + GetName (NamePreference preference = ePreferDemangled) const; + + //---------------------------------------------------------------------- + /// Check if "name" matches either the mangled or demangled name. + /// + /// @param[in] name + /// A name to match against both strings. + /// + /// @return + /// \b True if \a name matches either name, \b false otherwise. + //---------------------------------------------------------------------- + bool + NameMatches (const ConstString &name) const + { + if (m_mangled == name) + return true; + return GetDemangledName () == name; + } + + bool + NameMatches (const RegularExpression& regex) const; + + //---------------------------------------------------------------------- + /// Get the memory cost of this object. + /// + /// 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. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //---------------------------------------------------------------------- + size_t + MemorySize () const; + + //---------------------------------------------------------------------- + /// Set the string value in this object. + /// + /// If \a is_mangled is \b true, then the mangled named is set to \a + /// name, else the demangled name is set to \a name. + /// + /// @param[in] name + /// The already const version of the name for this object. + /// + /// @param[in] is_mangled + /// If \b true then \a name is a mangled name, if \b false then + /// \a name is demangled. + //---------------------------------------------------------------------- + void + SetValue (const ConstString &name, bool is_mangled); + + //---------------------------------------------------------------------- + /// Set the string value in this object. + /// + /// This version auto detects if the string is mangled by inspecting the + /// string value and looking for common mangling prefixes. + /// + /// @param[in] name + /// The already const version of the name for this object. + //---------------------------------------------------------------------- + void + SetValue (const ConstString &name); + +private: + //---------------------------------------------------------------------- + /// Mangled member variables. + //---------------------------------------------------------------------- + ConstString m_mangled; ///< The mangled version of the name + mutable ConstString m_demangled; ///< Mutable so we can get it on demand with a const version of this object +}; + + +Stream& operator << (Stream& s, const Mangled& obj); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Mangled_h_ diff --git a/include/lldb/Core/MappedHash.h b/include/lldb/Core/MappedHash.h new file mode 100644 index 00000000000..80d249d4cc9 --- /dev/null +++ b/include/lldb/Core/MappedHash.h @@ -0,0 +1,552 @@ +// +// MappedHash.h +// + +#ifndef liblldb_MappedHash_h_ +#define liblldb_MappedHash_h_ + +#include +#include + +#include +#include + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" + +class MappedHash +{ +public: + + enum HashFunctionType + { + eHashFunctionDJB = 0u // Daniel J Bernstein hash function that is also used by the ELF GNU_HASH sections + }; + + + static uint32_t + HashStringUsingDJB (const char *s) + { + uint32_t h = 5381; + + for (unsigned char c = *s; c; c = *++s) + h = ((h << 5) + h) + c; + + return h; + } + + static uint32_t + HashString (uint32_t hash_function, const char *s) + { + switch (hash_function) + { + case MappedHash::eHashFunctionDJB: + return HashStringUsingDJB (s); + + default: + break; + } + assert (!"Invalid hash function index"); + return 0; + } + + + static const uint32_t HASH_MAGIC = 0x48415348u; + static const uint32_t HASH_CIGAM = 0x48534148u; + + template + struct Header + { + typedef T HeaderData; + + uint32_t magic; // HASH_MAGIC or HASH_CIGAM magic value to allow endian detection + uint16_t version; // Version number + uint16_t hash_function; // The hash function enumeration that was used + uint32_t bucket_count; // The number of buckets in this hash table + uint32_t hashes_count; // The total number of unique hash values and hash data offsets in this table + uint32_t header_data_len; // The size in bytes of the "header_data" template member below + HeaderData header_data; // + + Header () : + magic (HASH_MAGIC), + version (1), + hash_function (eHashFunctionDJB), + bucket_count (0), + hashes_count (0), + header_data_len (sizeof(T)), + header_data () + { + } + + virtual + ~Header() + { + } + + size_t + GetByteSize() const + { + return sizeof(magic) + + sizeof(version) + + sizeof(hash_function) + + sizeof(bucket_count) + + sizeof(hashes_count) + + sizeof(header_data_len) + + header_data_len; + } + + virtual size_t + GetByteSize (const HeaderData &header_data) = 0; + + void + SetHeaderDataByteSize (uint32_t header_data_byte_size) + { + header_data_len = header_data_byte_size; + } + + void + Dump (lldb_private::Stream &s) + { + s.Printf ("header.magic = 0x%8.8x\n", magic); + s.Printf ("header.version = 0x%4.4x\n", version); + s.Printf ("header.hash_function = 0x%4.4x\n", hash_function); + s.Printf ("header.bucket_count = 0x%8.8x %u\n", bucket_count, bucket_count); + s.Printf ("header.hashes_count = 0x%8.8x %u\n", hashes_count, hashes_count); + s.Printf ("header.header_data_len = 0x%8.8x %u\n", header_data_len, header_data_len); + } + + virtual lldb::offset_t + Read (lldb_private::DataExtractor &data, lldb::offset_t offset) + { + if (data.ValidOffsetForDataOfSize (offset, + sizeof (magic) + + sizeof (version) + + sizeof (hash_function) + + sizeof (bucket_count) + + sizeof (hashes_count) + + sizeof (header_data_len))) + { + magic = data.GetU32 (&offset); + if (magic != HASH_MAGIC) + { + if (magic == HASH_CIGAM) + { + switch (data.GetByteOrder()) + { + case lldb::eByteOrderBig: + data.SetByteOrder(lldb::eByteOrderLittle); + break; + case lldb::eByteOrderLittle: + data.SetByteOrder(lldb::eByteOrderBig); + break; + default: + return LLDB_INVALID_OFFSET; + } + } + else + { + // Magic bytes didn't match + version = 0; + return LLDB_INVALID_OFFSET; + } + } + + version = data.GetU16 (&offset); + if (version != 1) + { + // Unsupported version + return LLDB_INVALID_OFFSET; + } + hash_function = data.GetU16 (&offset); + if (hash_function == 4) + hash_function = 0; // Deal with pre-release version of this table... + bucket_count = data.GetU32 (&offset); + hashes_count = data.GetU32 (&offset); + header_data_len = data.GetU32 (&offset); + return offset; + } + return LLDB_INVALID_OFFSET; + } +// +// // Returns a buffer that contains a serialized version of this table +// // that must be freed with free(). +// virtual void * +// Write (int fd); + }; + + template + class ExportTable + { + public: + typedef __HeaderDataType HeaderDataType; + typedef Header HeaderType; + typedef __KeyType KeyType; + typedef __ValueType ValueType; + + struct Entry + { + uint32_t hash; + KeyType key; + ValueType value; + }; + + typedef std::vector ValueArrayType; + + typedef std::map HashData; + // Map a name hash to one or more name infos + typedef std::map HashToHashData; + + virtual KeyType + GetKeyForStringType (const char *cstr) const = 0; + + virtual size_t + GetByteSize (const HashData &key_to_key_values) = 0; + + virtual bool + WriteHashData (const HashData &hash_data, + lldb_private::Stream &ostrm) = 0; +// + void + AddEntry (const char *cstr, const ValueType &value) + { + Entry entry; + entry.hash = MappedHash::HashString (eHashFunctionDJB, cstr); + entry.key = GetKeyForStringType (cstr); + entry.value = value; + m_entries.push_back (entry); + } + + void + Save (const HeaderDataType &header_data, + lldb_private::Stream &ostrm) + { + if (m_entries.empty()) + return; + + const uint32_t num_entries = m_entries.size(); + uint32_t i = 0; + + HeaderType header; + + header.magic = HASH_MAGIC; + header.version = 1; + header.hash_function = eHashFunctionDJB; + header.bucket_count = 0; + header.hashes_count = 0; + header.prologue_length = header_data.GetByteSize(); + + // We need to figure out the number of unique hashes first before we can + // calculate the number of buckets we want to use. + typedef std::vector hash_coll; + hash_coll unique_hashes; + unique_hashes.resize (num_entries); + for (i=0; i 1024) + header.bucket_count = num_unique_hashes/4; + else if (num_unique_hashes > 16) + header.bucket_count = num_unique_hashes/2; + else + header.bucket_count = num_unique_hashes; + if (header.bucket_count == 0) + header.bucket_count = 1; + + + std::vector hash_buckets; + std::vector hash_indexes (header.bucket_count, 0); + std::vector hash_values; + std::vector hash_offsets; + hash_buckets.resize (header.bucket_count); + uint32_t bucket_entry_empties = 0; + //StreamString hash_file_data(Stream::eBinary, dwarf->GetObjectFile()->GetAddressByteSize(), dwarf->GetObjectFile()->GetByteSize()); + + // Push all of the hashes into their buckets and create all bucket + // entries all populated with data. + for (i=0; ifirst); + hash_offsets.push_back (GetByteSize (pos->second)); + ++hash_count; + } + + hash_indexes[i] = hash_value_index; + } + } + header.hashes_count = hash_values.size(); + + // Write the header out now that we have the hash_count + header.Write (ostrm); + + // Now for each bucket we write the start index of the hashes + // for the current bucket, or UINT32_MAX if the bucket is empty + for (i=0; isecond); + } + } + } + } + protected: + typedef std::vector collection; + collection m_entries; + }; + // A class for reading and using a saved hash table from a block of data + // in memory + template + class MemoryTable + { + public: + typedef __HeaderType HeaderType; + typedef __KeyType KeyType; + typedef __HashData HashData; + + enum Result + { + eResultKeyMatch = 0u, // The entry was found, key matched and "pair" was filled in successfully + eResultKeyMismatch = 1u, // Bucket hash data collision, but key didn't match + eResultEndOfHashData = 2u, // The chain of items for this hash data in this bucket is terminated, search no more + eResultError = 3u // Error parsing the hash data, abort + }; + + struct Pair + { + KeyType key; + HashData value; + }; + + MemoryTable (lldb_private::DataExtractor &data) : + m_header (), + m_hash_indexes (NULL), + m_hash_values (NULL), + m_hash_offsets (NULL) + { + lldb::offset_t offset = m_header.Read (data, 0); + if (offset != LLDB_INVALID_OFFSET && IsValid ()) + { + m_hash_indexes = (uint32_t *)data.GetData (&offset, m_header.bucket_count * sizeof(uint32_t)); + m_hash_values = (uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); + m_hash_offsets = (uint32_t *)data.GetData (&offset, m_header.hashes_count * sizeof(uint32_t)); + } + } + + virtual + ~MemoryTable () + { + } + + bool + IsValid () const + { + return m_header.version == 1 && + m_header.hash_function == eHashFunctionDJB && + m_header.bucket_count > 0 && + m_header.hashes_count > 0; + } + + uint32_t + GetHashIndex (uint32_t bucket_idx) const + { + if (m_hash_indexes && bucket_idx < m_header.bucket_count) + return m_hash_indexes[bucket_idx]; + return UINT32_MAX; + } + + uint32_t + GetHashValue (uint32_t hash_idx) const + { + if (m_hash_values && hash_idx < m_header.hashes_count) + return m_hash_values[hash_idx]; + return UINT32_MAX; + } + + uint32_t + GetHashDataOffset (uint32_t hash_idx) const + { + if (m_hash_offsets && hash_idx < m_header.hashes_count) + return m_hash_offsets[hash_idx]; + return UINT32_MAX; + } + + bool + Find (const char *name, Pair &pair) const + { + if (IsValid ()) + { + const uint32_t bucket_count = m_header.bucket_count; + const uint32_t hash_count = m_header.hashes_count; + const uint32_t hash_value = MappedHash::HashString (m_header.hash_function, name); + const uint32_t bucket_idx = hash_value % bucket_count; + uint32_t hash_idx = GetHashIndex (bucket_idx); + if (hash_idx < hash_count) + { + for (; hash_idx < hash_count; ++hash_idx) + { + const uint32_t curr_hash_value = GetHashValue (hash_idx); + if (curr_hash_value == hash_value) + { + lldb::offset_t hash_data_offset = GetHashDataOffset (hash_idx); + while (hash_data_offset != UINT32_MAX) + { + const lldb::offset_t prev_hash_data_offset = hash_data_offset; + Result hash_result = GetHashDataForName (name, &hash_data_offset, pair); + // Check the result of getting our hash data + switch (hash_result) + { + case eResultKeyMatch: + return true; + + case eResultKeyMismatch: + if (prev_hash_data_offset == hash_data_offset) + return false; + break; + + case eResultEndOfHashData: + // The last HashData for this key has been reached, stop searching + return false; + case eResultError: + // Error parsing the hash data, abort + return false; + } + } + } + if ((curr_hash_value % bucket_count) != bucket_idx) + break; + } + } + } + return false; + } + + // This method must be implemented in any subclasses. + // The KeyType is user specified and must somehow result in a string + // value. For example, the KeyType might be a string offset in a string + // table and subclasses can store their string table as a member of the + // subclass and return a valie "const char *" given a "key". The value + // could also be a C string pointer, in which case just returning "key" + // will suffice. + + virtual const char * + GetStringForKeyType (KeyType key) const = 0; + + virtual bool + ReadHashData (uint32_t hash_data_offset, + HashData &hash_data) const = 0; + + // This method must be implemented in any subclasses and it must try to + // read one "Pair" at the offset pointed to by the "hash_data_offset_ptr" + // parameter. This offset should be updated as bytes are consumed and + // a value "Result" enum should be returned. If the "name" matches the + // full name for the "pair.key" (which must be filled in by this call), + // then the HashData in the pair ("pair.value") should be extracted and + // filled in and "eResultKeyMatch" should be returned. If "name" doesn't + // match this string for the key, then "eResultKeyMismatch" should be + // returned and all data for the current HashData must be consumed or + // skipped and the "hash_data_offset_ptr" offset needs to be updated to + // point to the next HashData. If the end of the HashData objects for + // a given hash value have been reached, then "eResultEndOfHashData" + // should be returned. If anything else goes wrong during parsing, + // return "eResultError" and the corresponding "Find()" function will + // be canceled and return false. + + virtual Result + GetHashDataForName (const char *name, + lldb::offset_t* hash_data_offset_ptr, + Pair &pair) const = 0; + + const HeaderType & + GetHeader() + { + return m_header; + } + + + void + ForEach (std::function const &callback) const + { + const size_t num_hash_offsets = m_header.hashes_count; + for (size_t i=0; i, + public SymbolContextScope +{ +public: + // Static functions that can track the lifetime of module objects. + // This is handy because we might have Module objects that are in + // shared pointers that aren't in the global module list (from + // ModuleList). If this is the case we need to know about it. + // The modules in the global list maintained by these functions + // can be viewed using the "target modules list" command using the + // "--global" (-g for short). + static size_t + GetNumberAllocatedModules (); + + static Module * + GetAllocatedModuleAtIndex (size_t idx); + + static Mutex * + GetAllocationModuleCollectionMutex(); + + //------------------------------------------------------------------ + /// Construct with file specification and architecture. + /// + /// Clients that wish to share modules with other targets should + /// use ModuleList::GetSharedModule(). + /// + /// @param[in] file_spec + /// The file specification for the on disk repesentation of + /// this executable image. + /// + /// @param[in] arch + /// The architecture to set as the current architecture in + /// this module. + /// + /// @param[in] object_name + /// The name of an object in a module used to extract a module + /// within a module (.a files and modules that contain multiple + /// architectures). + /// + /// @param[in] object_offset + /// The offset within an existing module used to extract a + /// module within a module (.a files and modules that contain + /// multiple architectures). + //------------------------------------------------------------------ + Module (const FileSpec& file_spec, + const ArchSpec& arch, + const ConstString *object_name = NULL, + off_t object_offset = 0, + const TimeValue *object_mod_time_ptr = NULL); + + Module (const ModuleSpec &module_spec); + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + virtual + ~Module (); + + bool + MatchesModuleSpec (const ModuleSpec &module_ref); + + //------------------------------------------------------------------ + /// Set the load address for all sections in a module to be the + /// file address plus \a slide. + /// + /// Many times a module will be loaded in a target with a constant + /// offset applied to all top level sections. This function can + /// set the load address for all top level sections to be the + /// section file address + offset. + /// + /// @param[in] target + /// The target in which to apply the section load addresses. + /// + /// @param[in] offset + /// The offset to apply to all file addresses for all top + /// level sections in the object file as each section load + /// address is being set. + /// + /// @param[out] changed + /// If any section load addresses were changed in \a target, + /// then \a changed will be set to \b true. Else \a changed + /// will be set to false. This allows this function to be + /// called multiple times on the same module for the same + /// target. If the module hasn't moved, then \a changed will + /// be false and no module updated notification will need to + /// be sent out. + /// + /// @return + /// /b True if any sections were successfully loaded in \a target, + /// /b false otherwise. + //------------------------------------------------------------------ + bool + SetLoadAddress (Target &target, + lldb::addr_t offset, + bool &changed); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + void + GetDescription (Stream *s, + lldb::DescriptionLevel level = lldb::eDescriptionLevelFull); + + //------------------------------------------------------------------ + /// Get the module path and object name. + /// + /// Modules can refer to object files. In this case the specification + /// is simple and would return the path to the file: + /// + /// "/usr/lib/foo.dylib" + /// + /// Modules can be .o files inside of a BSD archive (.a file). In + /// this case, the object specification will look like: + /// + /// "/usr/lib/foo.a(bar.o)" + /// + /// There are many places where logging wants to log this fully + /// qualified specification, so we centralize this functionality + /// here. + /// + /// @return + /// The object path + object name if there is one. + //------------------------------------------------------------------ + std::string + GetSpecificationDescription () const; + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. The dumped content will be only what has + /// been loaded or parsed up to this point at which this function + /// is called, so this is a good way to see what has been parsed + /// in a module. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext (Stream *s); + + + //------------------------------------------------------------------ + /// Find a symbol in the object file's symbol table. + /// + /// @param[in] name + /// The name of the symbol that we are looking for. + /// + /// @param[in] symbol_type + /// If set to eSymbolTypeAny, find a symbol of any type that + /// has a name that matches \a name. If set to any other valid + /// SymbolType enumeration value, then search only for + /// symbols that match \a symbol_type. + /// + /// @return + /// Returns a valid symbol pointer if a symbol was found, + /// NULL otherwise. + //------------------------------------------------------------------ + const Symbol * + FindFirstSymbolWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type = lldb::eSymbolTypeAny); + + size_t + FindSymbolsWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list); + + size_t + FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list); + + //------------------------------------------------------------------ + /// Find a funciton symbols in the object file's symbol table. + /// + /// @param[in] name + /// The name of the symbol that we are looking for. + /// + /// @param[in] name_type_mask + /// A mask that has one or more bitwise OR'ed values from the + /// lldb::FunctionNameType enumeration type that indicate what + /// kind of names we are looking for. + /// + /// @param[out] sc_list + /// A list to append any matching symbol contexts to. + /// + /// @return + /// The number of symbol contexts that were added to \a sc_list + //------------------------------------------------------------------ + size_t + FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find compile units by partial or full path. + /// + /// Finds all compile units that match \a path in all of the modules + /// and returns the results in \a sc_list. + /// + /// @param[in] path + /// The name of the function we are looking for. + /// + /// @param[in] append + /// If \b true, then append any compile units that were found + /// to \a sc_list. If \b false, then the \a sc_list is cleared + /// and the contents of \a sc_list are replaced. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list); + + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// If the function is an inlined function, it will have a block, + /// representing the inlined function, and the function will be the + /// containing function. If it is not inlined, then the block will + /// be NULL. + /// + /// @param[in] name + /// The name of the compile unit we are looking for. + /// + /// @param[in] namespace_decl + /// If valid, a namespace to search in. + /// + /// @param[in] name_type_mask + /// A bit mask of bits that indicate what kind of names should + /// be used when doing the lookup. Bits include fully qualified + /// names, base names, C++ methods, or ObjC selectors. + /// See FunctionNameType for more details. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a sc_list, else + /// matches replace the contents of \a sc_list. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindFunctions (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + uint32_t name_type_mask, + bool symbols_ok, + bool inlines_ok, + bool append, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find functions by name. + /// + /// If the function is an inlined function, it will have a block, + /// representing the inlined function, and the function will be the + /// containing function. If it is not inlined, then the block will + /// be NULL. + /// + /// @param[in] regex + /// A regular expression to use when matching the name. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a sc_list, else + /// matches replace the contents of \a sc_list. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindFunctions (const RegularExpression& regex, + bool symbols_ok, + bool inlines_ok, + bool append, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] namespace_decl + /// If valid, a namespace to search in. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variable_list); + + //------------------------------------------------------------------ + /// Find global and static variables by regular exression. + /// + /// @param[in] regex + /// A regular expression to use when matching the name. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list); + + //------------------------------------------------------------------ + /// Find types by name. + /// + /// Type lookups in modules go through the SymbolVendor (which will + /// use one or more SymbolFile subclasses). The SymbolFile needs to + /// be able to lookup types by basename and not the fully qualified + /// typename. This allows the type accelerator tables to stay small, + /// even with heavily templatized C++. The type search will then + /// narrow down the search results. If "exact_match" is true, then + /// the type search will only match exact type name matches. If + /// "exact_match" is false, the type will match as long as the base + /// typename matches and as long as any immediate containing + /// namespaces/class scopes that are specified match. So to search + /// for a type "d" in "b::c", the name "b::c::d" can be specified + /// and it will match any class/namespace "b" which contains a + /// class/namespace "c" which contains type "d". We do this to + /// allow users to not always have to specify complete scoping on + /// all expressions, but it also allows for exact matching when + /// required. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @param[in] type_name + /// The name of the type we are looking for that is a fully + /// or partially qualfieid type name. + /// + /// @param[in] exact_match + /// If \b true, \a type_name is fully qualifed and must match + /// exactly. If \b false, \a type_name is a partially qualfied + /// name where the leading namespaces or classes can be + /// omitted to make finding types that a user may type + /// easier. + /// + /// @param[out] type_list + /// A type list gets populated with any matches. + /// + /// @return + /// The number of matches added to \a type_list. + //------------------------------------------------------------------ + size_t + FindTypes (const SymbolContext& sc, + const ConstString &type_name, + bool exact_match, + size_t max_matches, + TypeList& types); + + lldb::TypeSP + FindFirstType (const SymbolContext& sc, + const ConstString &type_name, + bool exact_match); + + //------------------------------------------------------------------ + /// Find types by name that are in a namespace. This function is + /// used by the expression parser when searches need to happen in + /// an exact namespace scope. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @param[in] type_name + /// The name of a type within a namespace that should not include + /// any qualifying namespaces (just a type basename). + /// + /// @param[in] namespace_decl + /// The namespace declaration that this type must exist in. + /// + /// @param[out] type_list + /// A type list gets populated with any matches. + /// + /// @return + /// The number of matches added to \a type_list. + //------------------------------------------------------------------ + size_t + FindTypesInNamespace (const SymbolContext& sc, + const ConstString &type_name, + const ClangNamespaceDecl *namespace_decl, + size_t max_matches, + TypeList& type_list); + + //------------------------------------------------------------------ + /// Get const accessor for the module architecture. + /// + /// @return + /// A const reference to the architecture object. + //------------------------------------------------------------------ + const ArchSpec& + GetArchitecture () const; + + //------------------------------------------------------------------ + /// Get const accessor for the module file specification. + /// + /// This function returns the file for the module on the host system + /// that is running LLDB. This can differ from the path on the + /// platform since we might be doing remote debugging. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec & + GetFileSpec () const + { + return m_file; + } + + //------------------------------------------------------------------ + /// Get accessor for the module platform file specification. + /// + /// Platform file refers to the path of the module as it is known on + /// the remote system on which it is being debugged. For local + /// debugging this is always the same as Module::GetFileSpec(). But + /// remote debugging might mention a file "/usr/lib/liba.dylib" + /// which might be locally downloaded and cached. In this case the + /// platform file could be something like: + /// "/tmp/lldb/platform-cache/remote.host.computer/usr/lib/liba.dylib" + /// The file could also be cached in a local developer kit directory. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec & + GetPlatformFileSpec () const + { + if (m_platform_file) + return m_platform_file; + return m_file; + } + + void + SetPlatformFileSpec (const FileSpec &file) + { + m_platform_file = file; + } + + const FileSpec & + GetSymbolFileFileSpec () const + { + return m_symfile_spec; + } + + void + SetSymbolFileFileSpec (const FileSpec &file); + + const TimeValue & + GetModificationTime () const + { + return m_mod_time; + } + + const TimeValue & + GetObjectModificationTime () const + { + return m_object_mod_time; + } + + void + SetObjectModificationTime (const TimeValue &mod_time) + { + m_mod_time = mod_time; + } + + //------------------------------------------------------------------ + /// Tells whether this module is capable of being the main executable + /// for a process. + /// + /// @return + /// \b true if it is, \b false otherwise. + //------------------------------------------------------------------ + bool + IsExecutable (); + + //------------------------------------------------------------------ + /// Tells whether this module has been loaded in the target passed in. + /// This call doesn't distinguish between whether the module is loaded + /// by the dynamic loader, or by a "target module add" type call. + /// + /// @param[in] target + /// The target to check whether this is loaded in. + /// + /// @return + /// \b true if it is, \b false otherwise. + //------------------------------------------------------------------ + bool + IsLoadedInTarget (Target *target); + + bool + LoadScriptingResourceInTarget (Target *target, + Error& error, + Stream* feedback_stream = NULL); + + //------------------------------------------------------------------ + /// Get the number of compile units for this module. + /// + /// @return + /// The number of compile units that the symbol vendor plug-in + /// finds. + //------------------------------------------------------------------ + size_t + GetNumCompileUnits(); + + lldb::CompUnitSP + GetCompileUnitAtIndex (size_t idx); + + const ConstString & + GetObjectName() const; + + uint64_t + GetObjectOffset() const + { + return m_object_offset; + } + + //------------------------------------------------------------------ + /// Get the object file representation for the current architecture. + /// + /// If the object file has not been located or parsed yet, this + /// function will find the best ObjectFile plug-in that can parse + /// Module::m_file. + /// + /// @return + /// If Module::m_file does not exist, or no plug-in was found + /// that can parse the file, or the object file doesn't contain + /// the current architecture in Module::m_arch, NULL will be + /// returned, else a valid object file interface will be + /// returned. The returned pointer is owned by this object and + /// remains valid as long as the object is around. + //------------------------------------------------------------------ + virtual ObjectFile * + GetObjectFile (); + + //------------------------------------------------------------------ + /// Get the unified section list for the module. This is the section + /// list created by the module's object file and any debug info and + /// symbol files created by the symbol vendor. + /// + /// If the symbol vendor has not been loaded yet, this function + /// will return the section list for the object file. + /// + /// @return + /// Unified module section list. + //------------------------------------------------------------------ + virtual SectionList * + GetSectionList (); + + uint32_t + GetVersion (uint32_t *versions, uint32_t num_versions); + + // Load an object file from memory. + ObjectFile * + GetMemoryObjectFile (const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr, + Error &error); + //------------------------------------------------------------------ + /// Get the symbol vendor interface for the current architecture. + /// + /// If the symbol vendor file has not been located yet, this + /// function will find the best SymbolVendor plug-in that can + /// use the current object file. + /// + /// @return + /// If this module does not have a valid object file, or no + /// plug-in can be found that can use the object file, NULL will + /// be returned, else a valid symbol vendor plug-in interface + /// will be returned. The returned pointer is owned by this + /// object and remains valid as long as the object is around. + //------------------------------------------------------------------ + virtual SymbolVendor* + GetSymbolVendor(bool can_create = true, + lldb_private::Stream *feedback_strm = NULL); + + //------------------------------------------------------------------ + /// Get accessor the type list for this module. + /// + /// @return + /// A valid type list pointer, or NULL if there is no valid + /// symbol vendor for this module. + //------------------------------------------------------------------ + TypeList* + GetTypeList (); + + //------------------------------------------------------------------ + /// Get a pointer to the UUID value contained in this object. + /// + /// If the executable image file doesn't not have a UUID value built + /// into the file format, an MD5 checksum of the entire file, or + /// slice of the file for the current architecture should be used. + /// + /// @return + /// A const pointer to the internal copy of the UUID value in + /// this module if this module has a valid UUID value, NULL + /// otherwise. + //------------------------------------------------------------------ + const lldb_private::UUID & + GetUUID (); + + //------------------------------------------------------------------ + /// A debugging function that will cause everything in a module to + /// be parsed. + /// + /// All compile units will be pasred, along with all globals and + /// static variables and all functions for those compile units. + /// All types, scopes, local variables, static variables, global + /// variables, and line tables will be parsed. This can be used + /// prior to dumping a module to see a complete list of the + /// resuling debug information that gets parsed, or as a debug + /// function to ensure that the module can consume all of the + /// debug data the symbol vendor provides. + //------------------------------------------------------------------ + void + ParseAllDebugSymbols(); + + bool + ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr); + + uint32_t + ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc); + + //------------------------------------------------------------------ + /// Resolve items in the symbol context for a given file and line. + /// + /// Tries to resolve \a file_path and \a line to a list of matching + /// symbol contexts. + /// + /// The line table entries contains addresses that can be used to + /// further resolve the values in each match: the function, block, + /// symbol. Care should be taken to minimize the amount of + /// information that is requested to only what is needed -- + /// typically the module, compile unit, line table and line table + /// entry are sufficient. + /// + /// @param[in] file_path + /// A path to a source file to match. If \a file_path does not + /// specify a directory, then this query will match all files + /// whose base filename matches. If \a file_path does specify + /// a directory, the fullpath to the file must match. + /// + /// @param[in] line + /// The source line to match, or zero if just the compile unit + /// should be resolved. + /// + /// @param[in] check_inlines + /// Check for inline file and line number matches. This option + /// should be used sparingly as it will cause all line tables + /// for every compile unit to be parsed and searched for + /// matching inline file entries. + /// + /// @param[in] resolve_scope + /// The scope that should be resolved (see + /// SymbolContext::Scope). + /// + /// @param[out] sc_list + /// A symbol context list that gets matching symbols contexts + /// appended to. + /// + /// @return + /// The number of matches that were added to \a sc_list. + /// + /// @see SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForFilePath (const char *file_path, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Resolve items in the symbol context for a given file and line. + /// + /// Tries to resolve \a file_spec and \a line to a list of matching + /// symbol contexts. + /// + /// The line table entries contains addresses that can be used to + /// further resolve the values in each match: the function, block, + /// symbol. Care should be taken to minimize the amount of + /// information that is requested to only what is needed -- + /// typically the module, compile unit, line table and line table + /// entry are sufficient. + /// + /// @param[in] file_spec + /// A file spec to a source file to match. If \a file_path does + /// not specify a directory, then this query will match all + /// files whose base filename matches. If \a file_path does + /// specify a directory, the fullpath to the file must match. + /// + /// @param[in] line + /// The source line to match, or zero if just the compile unit + /// should be resolved. + /// + /// @param[in] check_inlines + /// Check for inline file and line number matches. This option + /// should be used sparingly as it will cause all line tables + /// for every compile unit to be parsed and searched for + /// matching inline file entries. + /// + /// @param[in] resolve_scope + /// The scope that should be resolved (see + /// SymbolContext::Scope). + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// A integer that contains SymbolContext::Scope bits set for + /// each item that was successfully resolved. + /// + /// @see SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list); + + + void + SetFileSpecAndObjectName (const FileSpec &file, + const ConstString &object_name); + + bool + GetIsDynamicLinkEditor () const + { + return m_is_dynamic_loader_module; + } + + void + SetIsDynamicLinkEditor (bool b) + { + m_is_dynamic_loader_module = b; + } + + ClangASTContext & + GetClangASTContext (); + + // Special error functions that can do printf style formatting that will prepend the message with + // something appropriate for this module (like the architecture, path and object name (if any)). + // This centralizes code so that everyone doesn't need to format their error and log messages on + // their own and keeps the output a bit more consistent. + void + LogMessage (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + void + LogMessageVerboseBacktrace (Log *log, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + void + ReportWarning (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + ReportError (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + // Only report an error once when the module is first detected to be modified + // so we don't spam the console with many messages. + void + ReportErrorIfModifyDetected (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + //------------------------------------------------------------------ + // Return true if the file backing this module has changed since the + // module was originally created since we saved the intial file + // modification time when the module first gets created. + //------------------------------------------------------------------ + bool + FileHasChanged () const; + + //------------------------------------------------------------------ + // SymbolVendor, SymbolFile and ObjectFile member objects should + // lock the module mutex to avoid deadlocks. + //------------------------------------------------------------------ + Mutex & + GetMutex () const + { + return m_mutex; + } + + PathMappingList & + GetSourceMappingList () + { + return m_source_mappings; + } + + const PathMappingList & + GetSourceMappingList () const + { + return m_source_mappings; + } + + //------------------------------------------------------------------ + /// Finds a source file given a file spec using the module source + /// path remappings (if any). + /// + /// Tries to resolve \a orig_spec by checking the module source path + /// remappings. It makes sure the file exists, so this call can be + /// expensive if the remappings are on a network file system, so + /// use this function sparingly (not in a tight debug info parsing + /// loop). + /// + /// @param[in] orig_spec + /// The original source file path to try and remap. + /// + /// @param[out] new_spec + /// The newly remapped filespec that is guaranteed to exist. + /// + /// @return + /// /b true if \a orig_spec was successfully located and + /// \a new_spec is filled in with an existing file spec, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const; + + //------------------------------------------------------------------ + /// Remaps a source file given \a path into \a new_path. + /// + /// Remaps \a path if any source remappings match. This function + /// does NOT stat the file system so it can be used in tight loops + /// where debug info is being parsed. + /// + /// @param[in] path + /// The original source file path to try and remap. + /// + /// @param[out] new_path + /// The newly remapped filespec that is may or may not exist. + /// + /// @return + /// /b true if \a path was successfully located and \a new_path + /// is filled in with a new source path, \b false otherwise. + //------------------------------------------------------------------ + bool + RemapSourceFile (const char *path, std::string &new_path) const; + + + //------------------------------------------------------------------ + /// Prepare to do a function name lookup. + /// + /// Looking up functions by name can be a tricky thing. LLDB requires + /// that accelerator tables contain full names for functions as well + /// as function basenames which include functions, class methods and + /// class functions. When the user requests that an action use a + /// function by name, we are sometimes asked to automatically figure + /// out what a name could possibly map to. A user might request a + /// breakpoint be set on "count". If no options are supplied to limit + /// the scope of where to search for count, we will by default match + /// any function names named "count", all class and instance methods + /// named "count" (no matter what the namespace or contained context) + /// and any selectors named "count". If a user specifies "a::b" we + /// will search for the basename "b", and then prune the results that + /// don't match "a::b" (note that "c::a::b" and "d::e::a::b" will + /// match a query of "a::b". + /// + /// @param[in] name + /// The user supplied name to use in the lookup + /// + /// @param[in] name_type_mask + /// The mask of bits from lldb::FunctionNameType enumerations + /// that tell us what kind of name we are looking for. + /// + /// @param[out] lookup_name + /// The actual name that will be used when calling + /// SymbolVendor::FindFunctions() or Symtab::FindFunctionSymbols() + /// + /// @param[out] lookup_name_type_mask + /// The actual name mask that should be used in the calls to + /// SymbolVendor::FindFunctions() or Symtab::FindFunctionSymbols() + /// + /// @param[out] match_name_after_lookup + /// A boolean that indicates if we need to iterate through any + /// match results obtained from SymbolVendor::FindFunctions() or + /// Symtab::FindFunctionSymbols() to see if the name contains + /// \a name. For example if \a name is "a::b", this function will + /// return a \a lookup_name of "b", with \a match_name_after_lookup + /// set to true to indicate any matches will need to be checked + /// to make sure they contain \a name. + //------------------------------------------------------------------ + static void + PrepareForFunctionNameLookup (const ConstString &name, + uint32_t name_type_mask, + ConstString &lookup_name, + uint32_t &lookup_name_type_mask, + bool &match_name_after_lookup); + +protected: + //------------------------------------------------------------------ + // Member Variables + //------------------------------------------------------------------ + mutable Mutex m_mutex; ///< A mutex to keep this object happy in multi-threaded environments. + TimeValue m_mod_time; ///< The modification time for this module when it was created. + ArchSpec m_arch; ///< The architecture for this module. + lldb_private::UUID m_uuid; ///< Each module is assumed to have a unique identifier to help match it up to debug symbols. + FileSpec m_file; ///< The file representation on disk for this module (if there is one). + FileSpec m_platform_file;///< The path to the module on the platform on which it is being debugged + FileSpec m_symfile_spec; ///< If this path is valid, then this is the file that _will_ be used as the symbol file for this module + ConstString m_object_name; ///< The name an object within this module that is selected, or empty of the module is represented by \a m_file. + uint64_t m_object_offset; + TimeValue m_object_mod_time; + lldb::ObjectFileSP m_objfile_sp; ///< A shared pointer to the object file parser for this module as it may or may not be shared with the SymbolFile + std::unique_ptr m_symfile_ap; ///< A pointer to the symbol vendor for this module. + ClangASTContext m_ast; ///< The AST context for this module. + PathMappingList m_source_mappings; ///< Module specific source remappings for when you have debug info for a module that doesn't match where the sources currently are + std::unique_ptr m_sections_ap; ///< Unified section list for module that is used by the ObjectFile and and ObjectFile instances for the debug info + + bool m_did_load_objfile:1, + m_did_load_symbol_vendor:1, + m_did_parse_uuid:1, + m_did_init_ast:1, + m_is_dynamic_loader_module:1; + mutable bool m_file_has_changed:1, + m_first_file_changed_log:1; /// See if the module was modified after it was initially opened. + + //------------------------------------------------------------------ + /// Resolve a file or load virtual address. + /// + /// Tries to resolve \a vm_addr as a file address (if \a + /// vm_addr_is_file_addr is true) or as a load address if \a + /// vm_addr_is_file_addr is false) in the symbol vendor. + /// \a resolve_scope indicates what clients wish to resolve + /// and can be used to limit the scope of what is parsed. + /// + /// @param[in] vm_addr + /// The load virtual address to resolve. + /// + /// @param[in] vm_addr_is_file_addr + /// If \b true, \a vm_addr is a file address, else \a vm_addr + /// if a load address. + /// + /// @param[in] resolve_scope + /// The scope that should be resolved (see + /// SymbolContext::Scope). + /// + /// @param[out] so_addr + /// The section offset based address that got resolved if + /// any bits are returned. + /// + /// @param[out] sc + // The symbol context that has objects filled in. Each bit + /// in the \a resolve_scope pertains to a member in the \a sc. + /// + /// @return + /// A integer that contains SymbolContext::Scope bits set for + /// each item that was successfully resolved. + /// + /// @see SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForAddress (lldb::addr_t vm_addr, + bool vm_addr_is_file_addr, + uint32_t resolve_scope, + Address& so_addr, + SymbolContext& sc); + + void + SymbolIndicesToSymbolContextList (Symtab *symtab, + std::vector &symbol_indexes, + SymbolContextList &sc_list); + + bool + SetArchitecture (const ArchSpec &new_arch); + + SectionList * + GetUnifiedSectionList(); + + friend class ModuleList; + friend class ObjectFile; + friend class SymbolFile; + +private: + + size_t + FindTypes_Impl (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types); + + + DISALLOW_COPY_AND_ASSIGN (Module); +}; + +} // namespace lldb_private + +#endif // liblldb_Module_h_ diff --git a/include/lldb/Core/ModuleChild.h b/include/lldb/Core/ModuleChild.h new file mode 100644 index 00000000000..d2a6fb0c3bc --- /dev/null +++ b/include/lldb/Core/ModuleChild.h @@ -0,0 +1,90 @@ +//===-- ModuleChild.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ModuleChild_h_ +#define liblldb_ModuleChild_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ModuleChild ModuleChild.h "lldb/Core/ModuleChild.h" +/// @brief A mix in class that contains a pointer back to the module +/// that owns the object which inherits from it. +//---------------------------------------------------------------------- +class ModuleChild +{ +public: + //------------------------------------------------------------------ + /// Construct with owning module. + /// + /// @param[in] module + /// The module that owns the object that inherits from this + /// class. + //------------------------------------------------------------------ + ModuleChild (const lldb::ModuleSP &module_sp); + + //------------------------------------------------------------------ + /// Copy constructor. + /// + /// @param[in] rhs + /// A const ModuleChild class reference to copy. + //------------------------------------------------------------------ + ModuleChild (const ModuleChild& rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~ModuleChild(); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// @param[in] rhs + /// A const ModuleChild class reference to copy. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ModuleChild& + operator= (const ModuleChild& rhs); + + //------------------------------------------------------------------ + /// Get const accessor for the module pointer. + /// + /// @return + /// A const pointer to the module that owns the object that + /// inherits from this class. + //------------------------------------------------------------------ + lldb::ModuleSP + GetModule () const; + + //------------------------------------------------------------------ + /// Set accessor for the module pointer. + /// + /// @param[in] module + /// A new module that owns the object that inherits from this + /// class. + //------------------------------------------------------------------ + void + SetModule (const lldb::ModuleSP &module_sp); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::ModuleWP m_module_wp; ///< The Module that owns the object that inherits + ///< from this class. +}; + +} // namespace lldb_private + + +#endif // liblldb_ModuleChild_h_ diff --git a/include/lldb/Core/ModuleList.h b/include/lldb/Core/ModuleList.h new file mode 100644 index 00000000000..1198e419648 --- /dev/null +++ b/include/lldb/Core/ModuleList.h @@ -0,0 +1,556 @@ +//===-- ModuleList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ModuleList_h_ +#define liblldb_ModuleList_h_ + +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ModuleList ModuleList.h "lldb/Core/ModuleList.h" +/// @brief A collection class for Module objects. +/// +/// Modules in the module collection class are stored as reference +/// counted shared pointers to Module objects. +//---------------------------------------------------------------------- +class ModuleList +{ +public: + + class Notifier + { + public: + virtual void + ModuleAdded (const ModuleList& module_list, const lldb::ModuleSP& module_sp) = 0; + virtual void + ModuleRemoved (const ModuleList& module_list, const lldb::ModuleSP& module_sp) = 0; + virtual void + ModuleUpdated (const ModuleList& module_list, const lldb::ModuleSP& old_module_sp, + const lldb::ModuleSP& new_module_sp) = 0; + virtual void + WillClearList (const ModuleList& module_list) = 0; + + virtual + ~Notifier () + {} + }; + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Creates an empty list of Module objects. + //------------------------------------------------------------------ + ModuleList (); + + //------------------------------------------------------------------ + /// Copy Constructor. + /// + /// Creates a new module list object with a copy of the modules from + /// \a rhs. + /// + /// @param[in] rhs + /// Another module list object. + //------------------------------------------------------------------ + ModuleList (const ModuleList& rhs); + + ModuleList (ModuleList::Notifier* notifier); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~ModuleList (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the module list from \a rhs into this list. + /// + /// @param[in] rhs + /// Another module list object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const ModuleList& + operator= (const ModuleList& rhs); + + //------------------------------------------------------------------ + /// Append a module to the module list. + /// + /// Appends the module to the collection. + /// + /// @param[in] module_sp + /// A shared pointer to a module to add to this collection. + //------------------------------------------------------------------ + void + Append (const lldb::ModuleSP &module_sp); + + //------------------------------------------------------------------ + /// Append a module to the module list and remove any equivalent + /// modules. Equivalent modules are ones whose file, platform file + /// and architecture matches. + /// + /// Replaces the module to the collection. + /// + /// @param[in] module_sp + /// A shared pointer to a module to replace in this collection. + //------------------------------------------------------------------ + void + ReplaceEquivalent (const lldb::ModuleSP &module_sp); + + bool + AppendIfNeeded (const lldb::ModuleSP &module_sp); + + void + Append (const ModuleList& module_list); + + bool + AppendIfNeeded (const ModuleList& module_list); + + bool + ReplaceModule (const lldb::ModuleSP &old_module_sp, const lldb::ModuleSP &new_module_sp); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears the list of modules and releases a reference to each + /// module object and if the reference count goes to zero, the + /// module will be deleted. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears the list of modules and releases a reference to each + /// module object and if the reference count goes to zero, the + /// module will be deleted. Also relese all memory that might be + /// held by any collection classes (like std::vector) + //------------------------------------------------------------------ + void + Destroy(); + //------------------------------------------------------------------ + /// Dump the description of each module contained in this list. + /// + /// Dump the description of each module contained in this list to + /// the supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @see Module::Dump(Stream *) const + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + void + LogUUIDAndPaths (Log *log, const char *prefix_cstr); + + Mutex & + GetMutex () const + { + return m_modules_mutex; + } + + size_t + GetIndexForModule (const Module *module) const; + + //------------------------------------------------------------------ + /// Get the module shared pointer for the module at index \a idx. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A shared pointer to a Module which can contain NULL if + /// \a idx is out of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + lldb::ModuleSP + GetModuleAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the module shared pointer for the module at index \a idx without + /// acquiring the ModuleList mutex. This MUST already have been + /// acquired with ModuleList::GetMutex and locked for this call to be safe. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A shared pointer to a Module which can contain NULL if + /// \a idx is out of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + lldb::ModuleSP + GetModuleAtIndexUnlocked (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the module pointer for the module at index \a idx. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A pointer to a Module which can by NULL if \a idx is out + /// of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + Module* + GetModulePointerAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Get the module pointer for the module at index \a idx without + /// acquiring the ModuleList mutex. This MUST already have been + /// acquired with ModuleList::GetMutex and locked for this call to be safe. + /// + /// @param[in] idx + /// An index into this module collection. + /// + /// @return + /// A pointer to a Module which can by NULL if \a idx is out + /// of range. + /// + /// @see ModuleList::GetSize() + //------------------------------------------------------------------ + Module* + GetModulePointerAtIndexUnlocked (size_t idx) const; + + //------------------------------------------------------------------ + /// Find compile units by partial or full path. + /// + /// Finds all compile units that match \a path in all of the modules + /// and returns the results in \a sc_list. + /// + /// @param[in] path + /// The name of the compile unit we are looking for. + /// + /// @param[in] append + /// If \b true, then append any compile units that were found + /// to \a sc_list. If \b false, then the \a sc_list is cleared + /// and the contents of \a sc_list are replaced. + /// + /// @param[out] sc_list + /// A symbol context list that gets filled in with all of the + /// matches. + /// + /// @return + /// The number of matches added to \a sc_list. + //------------------------------------------------------------------ + size_t + FindCompileUnits (const FileSpec &path, + bool append, + SymbolContextList &sc_list) const; + + //------------------------------------------------------------------ + /// @see Module::FindFunctions () + //------------------------------------------------------------------ + size_t + FindFunctions (const ConstString &name, + uint32_t name_type_mask, + bool include_symbols, + bool include_inlines, + bool append, + SymbolContextList &sc_list) const; + + //------------------------------------------------------------------ + /// @see Module::FindFunctionSymbols () + //------------------------------------------------------------------ + size_t + FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list); + + //------------------------------------------------------------------ + /// Find global and static variables by name. + /// + /// @param[in] name + /// The name of the global or static variable we are looking + /// for. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const ConstString &name, + bool append, + size_t max_matches, + VariableList& variable_list) const; + + //------------------------------------------------------------------ + /// Find global and static variables by regular exression. + /// + /// @param[in] regex + /// A regular expression to use when matching the name. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] variable_list + /// A list of variables that gets the matches appended to (if + /// \a append it \b true), or replace (if \a append is \b false). + /// + /// @return + /// The number of matches added to \a variable_list. + //------------------------------------------------------------------ + size_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list) const; + + //------------------------------------------------------------------ + /// Finds the first module whose file specification matches \a + /// file_spec. + /// + /// @param[in] file_spec_ptr + /// A file specification object to match against the Module's + /// file specifications. If \a file_spec does not have + /// directory information, matches will occur by matching only + /// the basename of any modules in this list. If this value is + /// NULL, then file specifications won't be compared when + /// searching for matching modules. + /// + /// @param[in] arch_ptr + /// The architecture to search for if non-NULL. If this value + /// is NULL no architecture matching will be performed. + /// + /// @param[in] uuid_ptr + /// The uuid to search for if non-NULL. If this value is NULL + /// no uuid matching will be performed. + /// + /// @param[in] object_name + /// An optional object name that must match as well. This value + /// can be NULL. + /// + /// @param[out] matching_module_list + /// A module list that gets filled in with any modules that + /// match the search criteria. + /// + /// @return + /// The number of matching modules found by the search. + //------------------------------------------------------------------ + size_t + FindModules (const ModuleSpec &module_spec, + ModuleList& matching_module_list) const; + + lldb::ModuleSP + FindModule (const Module *module_ptr) const; + + //------------------------------------------------------------------ + // Find a module by UUID + // + // The UUID value for a module is extracted from the ObjectFile and + // is the MD5 checksum, or a smarter object file equivalent, so + // finding modules by UUID values is very efficient and accurate. + //------------------------------------------------------------------ + lldb::ModuleSP + FindModule (const UUID &uuid) const; + + lldb::ModuleSP + FindFirstModule (const ModuleSpec &module_spec) const; + + size_t + FindSymbolsWithNameAndType (const ConstString &name, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append = false) const; + + size_t + FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append = false) const; + + //------------------------------------------------------------------ + /// Find types by name. + /// + /// @param[in] sc + /// A symbol context that scopes where to extract a type list + /// from. + /// + /// @param[in] name + /// The name of the type we are looking for. + /// + /// @param[in] append + /// If \b true, any matches will be appended to \a + /// variable_list, else matches replace the contents of + /// \a variable_list. + /// + /// @param[in] max_matches + /// Allow the number of matches to be limited to \a + /// max_matches. Specify UINT32_MAX to get all possible matches. + /// + /// @param[in] encoding + /// Limit the search to specific types, or get all types if + /// set to Type::invalid. + /// + /// @param[in] udt_name + /// If the encoding is a user defined type, specify the name + /// of the user defined type ("struct", "union", "class", etc). + /// + /// @param[out] type_list + /// A type list gets populated with any matches. + /// + /// @return + /// The number of matches added to \a type_list. + //------------------------------------------------------------------ + size_t + FindTypes (const SymbolContext& sc, + const ConstString &name, + bool name_is_fully_qualified, + size_t max_matches, + TypeList& types) const; + + bool + FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const; + + bool + Remove (const lldb::ModuleSP &module_sp); + + size_t + Remove (ModuleList &module_list); + + bool + RemoveIfOrphaned (const Module *module_ptr); + + size_t + RemoveOrphans (bool mandatory); + + bool + ResolveFileAddress (lldb::addr_t vm_addr, + Address& so_addr) const; + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextForAddress (const Address &,uint32_t,SymbolContext&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForAddress (const Address& so_addr, + uint32_t resolve_scope, + SymbolContext& sc) const; + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextForFilePath (const char *,uint32_t,bool,uint32_t,SymbolContextList&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextForFilePath (const char *file_path, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list) const; + + //------------------------------------------------------------------ + /// @copydoc Module::ResolveSymbolContextsForFileSpec (const FileSpec &,uint32_t,bool,uint32_t,SymbolContextList&) + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContextsForFileSpec (const FileSpec &file_spec, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list) const; + + //------------------------------------------------------------------ + /// Gets the size of the module list. + /// + /// @return + /// The number of modules in the module list. + //------------------------------------------------------------------ + size_t + GetSize () const; + + bool + LoadScriptingResourcesInTarget (Target *target, + std::list& errors, + Stream* feedback_stream = NULL, + bool continue_on_error = true); + + static bool + ModuleIsInCache (const Module *module_ptr); + + static Error + GetSharedModule (const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr, + bool always_create = false); + + static bool + RemoveSharedModule (lldb::ModuleSP &module_sp); + + static size_t + FindSharedModules (const ModuleSpec &module_spec, + ModuleList &matching_module_list); + + static size_t + RemoveOrphanSharedModules (bool mandatory); + + static bool + RemoveSharedModuleIfOrphaned (const Module *module_ptr); + +protected: + //------------------------------------------------------------------ + // Class typedefs. + //------------------------------------------------------------------ + typedef std::vector collection; ///< The module collection type. + + void + AppendImpl (const lldb::ModuleSP &module_sp, bool use_notifier = true); + + bool + RemoveImpl (const lldb::ModuleSP &module_sp, bool use_notifier = true); + + collection::iterator + RemoveImpl (collection::iterator pos, bool use_notifier = true); + + void + ClearImpl (bool use_notifier = true); + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_modules; ///< The collection of modules. + mutable Mutex m_modules_mutex; + + Notifier* m_notifier; + +}; + +} // namespace lldb_private + +#endif // liblldb_ModuleList_h_ diff --git a/include/lldb/Core/ModuleSpec.h b/include/lldb/Core/ModuleSpec.h new file mode 100644 index 00000000000..10e1ea9f17a --- /dev/null +++ b/include/lldb/Core/ModuleSpec.h @@ -0,0 +1,594 @@ +//===-- ModuleSpec.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ModuleSpec_h_ +#define liblldb_ModuleSpec_h_ + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Target/PathMappingList.h" + +namespace lldb_private { + +class ModuleSpec +{ +public: + ModuleSpec () : + m_file (), + m_platform_file (), + m_symbol_file (), + m_arch (), + m_uuid (), + m_object_name (), + m_object_offset (0), + m_object_mod_time (), + m_source_mappings () + { + } + + ModuleSpec (const FileSpec &file_spec) : + m_file (file_spec), + m_platform_file (), + m_symbol_file (), + m_arch (), + m_uuid (), + m_object_name (), + m_object_offset (0), + m_object_mod_time (), + m_source_mappings () + { + } + + ModuleSpec (const FileSpec &file_spec, const ArchSpec &arch) : + m_file (file_spec), + m_platform_file (), + m_symbol_file (), + m_arch (arch), + m_uuid (), + m_object_name (), + m_object_offset (0), + m_object_mod_time (), + m_source_mappings () + { + } + + ModuleSpec (const ModuleSpec &rhs) : + m_file (rhs.m_file), + m_platform_file (rhs.m_platform_file), + m_symbol_file (rhs.m_symbol_file), + m_arch (rhs.m_arch), + m_uuid (rhs.m_uuid), + m_object_name (rhs.m_object_name), + m_object_offset (rhs.m_object_offset), + m_object_mod_time (rhs.m_object_mod_time), + m_source_mappings (rhs.m_source_mappings) + { + } + + ModuleSpec & + operator = (const ModuleSpec &rhs) + { + if (this != &rhs) + { + m_file = rhs.m_file; + m_platform_file = rhs.m_platform_file; + m_symbol_file = rhs.m_symbol_file; + m_arch = rhs.m_arch; + m_uuid = rhs.m_uuid; + m_object_name = rhs.m_object_name; + m_object_offset = rhs.m_object_offset; + m_object_mod_time = rhs.m_object_mod_time; + m_source_mappings = rhs.m_source_mappings; + } + return *this; + } + + FileSpec * + GetFileSpecPtr () + { + if (m_file) + return &m_file; + return NULL; + } + + const FileSpec * + GetFileSpecPtr () const + { + if (m_file) + return &m_file; + return NULL; + } + + FileSpec & + GetFileSpec () + { + return m_file; + } + const FileSpec & + GetFileSpec () const + { + return m_file; + } + + FileSpec * + GetPlatformFileSpecPtr () + { + if (m_platform_file) + return &m_platform_file; + return NULL; + } + + const FileSpec * + GetPlatformFileSpecPtr () const + { + if (m_platform_file) + return &m_platform_file; + return NULL; + } + + FileSpec & + GetPlatformFileSpec () + { + return m_platform_file; + } + + const FileSpec & + GetPlatformFileSpec () const + { + return m_platform_file; + } + + FileSpec * + GetSymbolFileSpecPtr () + { + if (m_symbol_file) + return &m_symbol_file; + return NULL; + } + + const FileSpec * + GetSymbolFileSpecPtr () const + { + if (m_symbol_file) + return &m_symbol_file; + return NULL; + } + + FileSpec & + GetSymbolFileSpec () + { + return m_symbol_file; + } + + const FileSpec & + GetSymbolFileSpec () const + { + return m_symbol_file; + } + + + ArchSpec * + GetArchitecturePtr () + { + if (m_arch.IsValid()) + return &m_arch; + return NULL; + } + + const ArchSpec * + GetArchitecturePtr () const + { + if (m_arch.IsValid()) + return &m_arch; + return NULL; + } + + ArchSpec & + GetArchitecture () + { + return m_arch; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + UUID * + GetUUIDPtr () + { + if (m_uuid.IsValid()) + return &m_uuid; + return NULL; + } + + const UUID * + GetUUIDPtr () const + { + if (m_uuid.IsValid()) + return &m_uuid; + return NULL; + } + + UUID & + GetUUID () + { + return m_uuid; + } + + const UUID & + GetUUID () const + { + return m_uuid; + } + + ConstString & + GetObjectName () + { + return m_object_name; + } + + const ConstString & + GetObjectName () const + { + return m_object_name; + } + + uint64_t + GetObjectOffset () const + { + return m_object_offset; + } + + void + SetObjectOffset (uint64_t object_offset) + { + m_object_offset = object_offset; + } + + TimeValue & + GetObjectModificationTime () + { + return m_object_mod_time; + } + + const TimeValue & + GetObjectModificationTime () const + { + return m_object_mod_time; + } + + PathMappingList & + GetSourceMappingList () const + { + return m_source_mappings; + } + + void + Clear () + { + m_file.Clear(); + m_platform_file.Clear(); + m_symbol_file.Clear(); + m_arch.Clear(); + m_uuid.Clear(); + m_object_name.Clear(); + m_object_offset = 0; + m_source_mappings.Clear(false); + m_object_mod_time.Clear(); + } + + + operator bool () const + { + if (m_file) + return true; + if (m_platform_file) + return true; + if (m_symbol_file) + return true; + if (m_arch.IsValid()) + return true; + if (m_uuid.IsValid()) + return true; + if (m_object_name) + return true; + if (m_object_mod_time.IsValid()) + return true; + return false; + } + + void + Dump (Stream &strm) + { + bool dumped_something = false; + if (m_file) + { + strm.PutCString("file = '"); + strm << m_file; + strm.PutCString("'"); + dumped_something = true; + } + if (m_platform_file) + { + if (dumped_something) + strm.PutCString(", "); + strm.PutCString("platform_file = '"); + strm << m_platform_file; + strm.PutCString("'"); + dumped_something = true; + } + if (m_symbol_file) + { + if (dumped_something) + strm.PutCString(", "); + strm.PutCString("symbol_file = '"); + strm << m_symbol_file; + strm.PutCString("'"); + dumped_something = true; + } + if (m_arch.IsValid()) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("arch = %s", m_arch.GetTriple().str().c_str()); + dumped_something = true; + } + if (m_uuid.IsValid()) + { + if (dumped_something) + strm.PutCString(", "); + strm.PutCString("uuid = "); + m_uuid.Dump(&strm); + dumped_something = true; + } + if (m_object_name) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("object_name = %s", m_object_name.GetCString()); + dumped_something = true; + } + if (m_object_offset > 0) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("object_offset = 0x%" PRIx64, m_object_offset); + dumped_something = true; + } + if (m_object_mod_time.IsValid()) + { + if (dumped_something) + strm.PutCString(", "); + strm.Printf("object_mod_time = 0x%" PRIx64, m_object_mod_time.GetAsSecondsSinceJan1_1970()); + dumped_something = true; + } + } + + bool + Matches (const ModuleSpec &match_module_spec, bool exact_arch_match) const + { + if (match_module_spec.GetUUIDPtr() && match_module_spec.GetUUID() != GetUUID()) + return false; + if (match_module_spec.GetObjectName() && match_module_spec.GetObjectName() != GetObjectName()) + return false; + if (match_module_spec.GetFileSpecPtr()) + { + const FileSpec &fspec = match_module_spec.GetFileSpec(); + if (!FileSpec::Equal(fspec, GetFileSpec(), fspec.GetDirectory().IsEmpty() == false)) + return false; + } + if (match_module_spec.GetPlatformFileSpecPtr()) + { + const FileSpec &fspec = match_module_spec.GetPlatformFileSpec(); + if (!FileSpec::Equal(fspec, GetPlatformFileSpec(), fspec.GetDirectory().IsEmpty() == false)) + return false; + + } + if (match_module_spec.GetSymbolFileSpecPtr()) + { + const FileSpec &fspec = match_module_spec.GetSymbolFileSpec(); + if (!FileSpec::Equal(fspec, GetSymbolFileSpec(), fspec.GetDirectory().IsEmpty() == false)) + return false; + + } + if (match_module_spec.GetArchitecturePtr()) + { + if (exact_arch_match) + { + if (!GetArchitecture().IsExactMatch(match_module_spec.GetArchitecture())) + return false; + } + else + { + if (!GetArchitecture().IsCompatibleMatch(match_module_spec.GetArchitecture())) + return false; + } + } + return true; + } + +protected: + FileSpec m_file; + FileSpec m_platform_file; + FileSpec m_symbol_file; + ArchSpec m_arch; + UUID m_uuid; + ConstString m_object_name; + uint64_t m_object_offset; + TimeValue m_object_mod_time; + mutable PathMappingList m_source_mappings; +}; + +class ModuleSpecList +{ +public: + ModuleSpecList () : + m_specs(), + m_mutex(Mutex::eMutexTypeRecursive) + { + } + + ModuleSpecList (const ModuleSpecList &rhs) : + m_specs(), + m_mutex(Mutex::eMutexTypeRecursive) + { + Mutex::Locker lhs_locker(m_mutex); + Mutex::Locker rhs_locker(rhs.m_mutex); + m_specs = rhs.m_specs; + } + + ~ModuleSpecList () + { + } + + ModuleSpecList & + operator = (const ModuleSpecList &rhs) + { + if (this != &rhs) + { + Mutex::Locker lhs_locker(m_mutex); + Mutex::Locker rhs_locker(rhs.m_mutex); + m_specs = rhs.m_specs; + } + return *this; + } + + size_t + GetSize() const + { + Mutex::Locker locker(m_mutex); + return m_specs.size(); + } + + void + Clear () + { + Mutex::Locker locker(m_mutex); + m_specs.clear(); + } + + void + Append (const ModuleSpec &spec) + { + Mutex::Locker locker(m_mutex); + m_specs.push_back (spec); + } + + void + Append (const ModuleSpecList &rhs) + { + Mutex::Locker lhs_locker(m_mutex); + Mutex::Locker rhs_locker(rhs.m_mutex); + m_specs.insert(m_specs.end(), rhs.m_specs.begin(), rhs.m_specs.end()); + } + + // The index "i" must be valid and this can't be used in + // multi-threaded code as no mutex lock is taken. + ModuleSpec & + GetModuleSpecRefAtIndex (size_t i) + { + return m_specs[i]; + } + bool + GetModuleSpecAtIndex (size_t i, ModuleSpec &module_spec) const + { + Mutex::Locker locker(m_mutex); + if (i < m_specs.size()) + { + module_spec = m_specs[i]; + return true; + } + module_spec.Clear(); + return false; + } + + + bool + FindMatchingModuleSpec (const ModuleSpec &module_spec, ModuleSpec &match_module_spec) const + { + Mutex::Locker locker(m_mutex); + bool exact_arch_match = true; + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + { + match_module_spec = spec; + return true; + } + } + + // If there was an architecture, retry with a compatible arch + if (module_spec.GetArchitecturePtr()) + { + exact_arch_match = false; + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + { + match_module_spec = spec; + return true; + } + } + } + match_module_spec.Clear(); + return false; + } + + size_t + FindMatchingModuleSpecs (const ModuleSpec &module_spec, ModuleSpecList &matching_list) const + { + Mutex::Locker locker(m_mutex); + bool exact_arch_match = true; + const size_t initial_match_count = matching_list.GetSize(); + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + matching_list.Append (spec); + } + + // If there was an architecture, retry with a compatible arch if no matches were found + if (module_spec.GetArchitecturePtr() && (initial_match_count == matching_list.GetSize())) + { + exact_arch_match = false; + for (auto spec: m_specs) + { + if (spec.Matches(module_spec, exact_arch_match)) + matching_list.Append (spec); + } + } + return matching_list.GetSize() - initial_match_count; + } + + void + Dump (Stream &strm) + { + Mutex::Locker locker(m_mutex); + uint32_t idx = 0; + for (auto spec: m_specs) + { + strm.Printf("[%u] ", idx); + spec.Dump (strm); + strm.EOL(); + ++idx; + } + } + +protected: + typedef std::vector collection; ///< The module collection type. + collection m_specs; ///< The collection of modules. + mutable Mutex m_mutex; +}; + +} // namespace lldb_private + +#endif // liblldb_ModuleSpec_h_ diff --git a/include/lldb/Core/Opcode.h b/include/lldb/Core/Opcode.h new file mode 100644 index 00000000000..c07193b6205 --- /dev/null +++ b/include/lldb/Core/Opcode.h @@ -0,0 +1,270 @@ +//===-- Opcode.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Opcode_h +#define lldb_Opcode_h + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" + +namespace lldb +{ + class SBInstruction; +} + +namespace lldb_private { + + class Opcode + { + public: + enum Type + { + eTypeInvalid, + eType8, + eType16, + eType16_2, // a 32-bit Thumb instruction, made up of two words + eType32, + eType64, + eTypeBytes + }; + + Opcode () : m_type (eTypeInvalid) + { + } + + Opcode (uint8_t inst) : m_type (eType8) + { + m_data.inst8 = inst; + } + + Opcode (uint16_t inst) : m_type (eType16) + { + m_data.inst16 = inst; + } + + Opcode (uint32_t inst) : m_type (eType32) + { + m_data.inst32 = inst; + } + + Opcode (uint64_t inst) : m_type (eType64) + { + m_data.inst64 = inst; + } + + Opcode (uint8_t *bytes, size_t length) + { + SetOpcodeBytes (bytes, length); + } + + void + Clear() + { + m_type = Opcode::eTypeInvalid; + } + Opcode::Type + GetType () const + { + return m_type; + } + + uint8_t + GetOpcode8 (uint8_t invalid_opcode = UINT8_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: break; + case Opcode::eType16_2: break; + case Opcode::eType32: break; + case Opcode::eType64: break; + case Opcode::eTypeBytes: break; + break; + } + return invalid_opcode; + } + + uint16_t + GetOpcode16 (uint16_t invalid_opcode = UINT16_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: return m_data.inst16; + case Opcode::eType16_2: break; + case Opcode::eType32: break; + case Opcode::eType64: break; + case Opcode::eTypeBytes: break; + } + return invalid_opcode; + } + + uint32_t + GetOpcode32 (uint32_t invalid_opcode = UINT32_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: return m_data.inst16; + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return m_data.inst32; + case Opcode::eType64: break; + case Opcode::eTypeBytes: break; + } + return invalid_opcode; + } + + uint64_t + GetOpcode64 (uint64_t invalid_opcode = UINT64_MAX) const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return m_data.inst8; + case Opcode::eType16: return m_data.inst16; + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return m_data.inst32; + case Opcode::eType64: return m_data.inst64; + case Opcode::eTypeBytes: break; + } + return invalid_opcode; + } + + void + SetOpcode8 (uint8_t inst) + { + m_type = eType8; + m_data.inst8 = inst; + } + + void + SetOpcode16 (uint16_t inst) + { + m_type = eType16; + m_data.inst16 = inst; + } + + void + SetOpcode16_2 (uint32_t inst) + { + m_type = eType16_2; + m_data.inst32 = inst; + } + + void + SetOpcode32 (uint32_t inst) + { + m_type = eType32; + m_data.inst32 = inst; + } + + void + SetOpcode64 (uint64_t inst) + { + m_type = eType64; + m_data.inst64 = inst; + } + + void + SetOpcodeBytes (const void *bytes, size_t length) + { + if (bytes && length > 0) + { + m_type = eTypeBytes; + m_data.inst.length = length; + assert (length < sizeof (m_data.inst.bytes)); + memcpy (m_data.inst.bytes, bytes, length); + } + else + { + m_type = eTypeInvalid; + m_data.inst.length = 0; + } + } + + int + Dump (Stream *s, uint32_t min_byte_width); + + const void * + GetOpcodeBytes () const + { + if (m_type == Opcode::eTypeBytes) + return m_data.inst.bytes; + return NULL; + } + + uint32_t + GetByteSize () const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return sizeof(m_data.inst8); + case Opcode::eType16: return sizeof(m_data.inst16); + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return sizeof(m_data.inst32); + case Opcode::eType64: return sizeof(m_data.inst64); + case Opcode::eTypeBytes: return m_data.inst.length; + } + return 0; + } + + // Get the opcode exactly as it would be laid out in memory. + uint32_t + GetData (DataExtractor &data) const; + + protected: + + friend class lldb::SBInstruction; + + const void * + GetOpcodeDataBytes () const + { + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: return &m_data.inst8; + case Opcode::eType16: return &m_data.inst16; + case Opcode::eType16_2: // passthrough + case Opcode::eType32: return &m_data.inst32; + case Opcode::eType64: return &m_data.inst64; + case Opcode::eTypeBytes: return m_data.inst.bytes; + } + return NULL; + } + + lldb::ByteOrder + GetDataByteOrder () const; + + Opcode::Type m_type; + union + { + uint8_t inst8; + uint16_t inst16; + uint32_t inst32; + uint64_t inst64; + struct + { + uint8_t bytes[16]; // This must be big enough to handle any opcode for any supported target. + uint8_t length; + } inst; + } m_data; + }; + +} // namespace lldb_private + +#endif // lldb_Opcode_h diff --git a/include/lldb/Core/PluginInterface.h b/include/lldb/Core/PluginInterface.h new file mode 100644 index 00000000000..19371ca9860 --- /dev/null +++ b/include/lldb/Core/PluginInterface.h @@ -0,0 +1,37 @@ +//===-- PluginInterface.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PluginInterface_h_ +#define liblldb_PluginInterface_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class PluginInterface +{ +public: + virtual + ~PluginInterface () {} + + virtual ConstString + GetPluginName() = 0; + + virtual uint32_t + GetPluginVersion() = 0; + +}; + +} // namespace lldb_private + +#endif // liblldb_PluginInterface_h_ diff --git a/include/lldb/Core/PluginManager.h b/include/lldb/Core/PluginManager.h new file mode 100644 index 00000000000..91f8fbb997f --- /dev/null +++ b/include/lldb/Core/PluginManager.h @@ -0,0 +1,352 @@ +//===-- PluginManager.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_PluginManager_h_ +#define liblldb_PluginManager_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +class PluginManager +{ +public: + static void + Initialize (); + + static void + Terminate (); + + //------------------------------------------------------------------ + // ABI + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ABICreateInstance create_callback); + + static bool + UnregisterPlugin (ABICreateInstance create_callback); + + static ABICreateInstance + GetABICreateCallbackAtIndex (uint32_t idx); + + static ABICreateInstance + GetABICreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // Disassembler + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + DisassemblerCreateInstance create_callback); + + static bool + UnregisterPlugin (DisassemblerCreateInstance create_callback); + + static DisassemblerCreateInstance + GetDisassemblerCreateCallbackAtIndex (uint32_t idx); + + static DisassemblerCreateInstance + GetDisassemblerCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // DynamicLoader + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + DynamicLoaderCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = NULL); + + static bool + UnregisterPlugin (DynamicLoaderCreateInstance create_callback); + + static DynamicLoaderCreateInstance + GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx); + + static DynamicLoaderCreateInstance + GetDynamicLoaderCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // EmulateInstruction + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + EmulateInstructionCreateInstance create_callback); + + static bool + UnregisterPlugin (EmulateInstructionCreateInstance create_callback); + + static EmulateInstructionCreateInstance + GetEmulateInstructionCreateCallbackAtIndex (uint32_t idx); + + static EmulateInstructionCreateInstance + GetEmulateInstructionCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // OperatingSystem + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + OperatingSystemCreateInstance create_callback); + + static bool + UnregisterPlugin (OperatingSystemCreateInstance create_callback); + + static OperatingSystemCreateInstance + GetOperatingSystemCreateCallbackAtIndex (uint32_t idx); + + static OperatingSystemCreateInstance + GetOperatingSystemCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // LanguageRuntime + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + LanguageRuntimeCreateInstance create_callback); + + static bool + UnregisterPlugin (LanguageRuntimeCreateInstance create_callback); + + static LanguageRuntimeCreateInstance + GetLanguageRuntimeCreateCallbackAtIndex (uint32_t idx); + + static LanguageRuntimeCreateInstance + GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // ObjectFile + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ObjectFileCreateInstance create_callback, + ObjectFileCreateMemoryInstance create_memory_callback, + ObjectFileGetModuleSpecifications get_module_specifications); + + static bool + UnregisterPlugin (ObjectFileCreateInstance create_callback); + + static ObjectFileCreateInstance + GetObjectFileCreateCallbackAtIndex (uint32_t idx); + + static ObjectFileCreateMemoryInstance + GetObjectFileCreateMemoryCallbackAtIndex (uint32_t idx); + + static ObjectFileGetModuleSpecifications + GetObjectFileGetModuleSpecificationsCallbackAtIndex (uint32_t idx); + + static ObjectFileCreateInstance + GetObjectFileCreateCallbackForPluginName (const ConstString &name); + + static ObjectFileCreateMemoryInstance + GetObjectFileCreateMemoryCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // ObjectContainer + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ObjectContainerCreateInstance create_callback, + ObjectFileGetModuleSpecifications get_module_specifications); + + static bool + UnregisterPlugin (ObjectContainerCreateInstance create_callback); + + static ObjectContainerCreateInstance + GetObjectContainerCreateCallbackAtIndex (uint32_t idx); + + static ObjectContainerCreateInstance + GetObjectContainerCreateCallbackForPluginName (const ConstString &name); + + static ObjectFileGetModuleSpecifications + GetObjectContainerGetModuleSpecificationsCallbackAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // LogChannel + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + LogChannelCreateInstance create_callback); + + static bool + UnregisterPlugin (LogChannelCreateInstance create_callback); + + static LogChannelCreateInstance + GetLogChannelCreateCallbackAtIndex (uint32_t idx); + + static LogChannelCreateInstance + GetLogChannelCreateCallbackForPluginName (const ConstString &name); + + static const char * + GetLogChannelCreateNameAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // Platform + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + PlatformCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = NULL); + + static bool + UnregisterPlugin (PlatformCreateInstance create_callback); + + static PlatformCreateInstance + GetPlatformCreateCallbackAtIndex (uint32_t idx); + + static PlatformCreateInstance + GetPlatformCreateCallbackForPluginName (const ConstString &name); + + static const char * + GetPlatformPluginNameAtIndex (uint32_t idx); + + static const char * + GetPlatformPluginDescriptionAtIndex (uint32_t idx); + + static size_t + AutoCompletePlatformName (const char *partial_name, + StringList &matches); + //------------------------------------------------------------------ + // Process + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + ProcessCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback = NULL); + + static bool + UnregisterPlugin (ProcessCreateInstance create_callback); + + static ProcessCreateInstance + GetProcessCreateCallbackAtIndex (uint32_t idx); + + static ProcessCreateInstance + GetProcessCreateCallbackForPluginName (const ConstString &name); + + static const char * + GetProcessPluginNameAtIndex (uint32_t idx); + + static const char * + GetProcessPluginDescriptionAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + // SymbolFile + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + SymbolFileCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolFileCreateInstance create_callback); + + static SymbolFileCreateInstance + GetSymbolFileCreateCallbackAtIndex (uint32_t idx); + + static SymbolFileCreateInstance + GetSymbolFileCreateCallbackForPluginName (const ConstString &name); + + + //------------------------------------------------------------------ + // SymbolVendor + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + SymbolVendorCreateInstance create_callback); + + static bool + UnregisterPlugin (SymbolVendorCreateInstance create_callback); + + static SymbolVendorCreateInstance + GetSymbolVendorCreateCallbackAtIndex (uint32_t idx); + + static SymbolVendorCreateInstance + GetSymbolVendorCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // UnwindAssembly + //------------------------------------------------------------------ + static bool + RegisterPlugin (const ConstString &name, + const char *description, + UnwindAssemblyCreateInstance create_callback); + + static bool + UnregisterPlugin (UnwindAssemblyCreateInstance create_callback); + + static UnwindAssemblyCreateInstance + GetUnwindAssemblyCreateCallbackAtIndex (uint32_t idx); + + static UnwindAssemblyCreateInstance + GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name); + + //------------------------------------------------------------------ + // Some plug-ins might register a DebuggerInitializeCallback + // callback when registering the plug-in. After a new Debugger + // instance is created, this DebuggerInitialize function will get + // called. This allows plug-ins to install Properties and do any + // other initialization that requires a debugger instance. + //------------------------------------------------------------------ + static void + DebuggerInitialize (Debugger &debugger); + + static lldb::OptionValuePropertiesSP + GetSettingForDynamicLoaderPlugin (Debugger &debugger, + const ConstString &setting_name); + + static bool + CreateSettingForDynamicLoaderPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property); + + static lldb::OptionValuePropertiesSP + GetSettingForPlatformPlugin (Debugger &debugger, + const ConstString &setting_name); + + static bool + CreateSettingForPlatformPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property); + + static lldb::OptionValuePropertiesSP + GetSettingForProcessPlugin (Debugger &debugger, + const ConstString &setting_name); + + static bool + CreateSettingForProcessPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property); + +}; + + +} // namespace lldb_private + +#endif // liblldb_PluginManager_h_ diff --git a/include/lldb/Core/RangeMap.h b/include/lldb/Core/RangeMap.h new file mode 100644 index 00000000000..ee42467c18b --- /dev/null +++ b/include/lldb/Core/RangeMap.h @@ -0,0 +1,1543 @@ +//===-- RangeMap.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RangeMap_h_ +#define liblldb_RangeMap_h_ + +#include + +#include "lldb/lldb-private.h" +#include "llvm/ADT/SmallVector.h" + +// Uncomment to make sure all Range objects are sorted when needed +//#define ASSERT_RANGEMAP_ARE_SORTED + +namespace lldb_private { + + + //---------------------------------------------------------------------- + // Templatized classes for dealing with generic ranges and also + // collections of ranges, or collections of ranges that have associated + // data. + //---------------------------------------------------------------------- + + //---------------------------------------------------------------------- + // A simple range class where you get to define the type of the range + // base "B", and the type used for the range byte size "S". + //---------------------------------------------------------------------- + template + struct Range + { + typedef B BaseType; + typedef S SizeType; + + BaseType base; + SizeType size; + + Range () : + base (0), + size (0) + { + } + + Range (BaseType b, SizeType s) : + base (b), + size (s) + { + } + + void + Clear (BaseType b = 0) + { + base = b; + size = 0; + } + + // Set the start value for the range, and keep the same size + BaseType + GetRangeBase () const + { + return base; + } + + void + SetRangeBase (BaseType b) + { + base = b; + } + + void + Slide (BaseType slide) + { + base += slide; + } + + BaseType + GetRangeEnd () const + { + return base + size; + } + + void + SetRangeEnd (BaseType end) + { + if (end > base) + size = end - base; + else + size = 0; + } + + SizeType + GetByteSize () const + { + return size; + } + + void + SetByteSize (SizeType s) + { + size = s; + } + + bool + IsValid() const + { + return size > 0; + } + + bool + Contains (BaseType r) const + { + return (GetRangeBase() <= r) && (r < GetRangeEnd()); + } + + bool + ContainsEndInclusive (BaseType r) const + { + return (GetRangeBase() <= r) && (r <= GetRangeEnd()); + } + + bool + Contains (const Range& range) const + { + return Contains(range.GetRangeBase()) && ContainsEndInclusive(range.GetRangeEnd()); + } + + bool + Overlap (const Range &rhs) const + { + const BaseType lhs_base = this->GetRangeBase(); + const BaseType rhs_base = rhs.GetRangeBase(); + const BaseType lhs_end = this->GetRangeEnd(); + const BaseType rhs_end = rhs.GetRangeEnd(); + bool result = (lhs_base <= rhs_end) && (lhs_end >= rhs_base); + return result; + } + + bool + operator < (const Range &rhs) const + { + if (base == rhs.base) + return size < rhs.size; + return base < rhs.base; + } + + bool + operator == (const Range &rhs) const + { + return base == rhs.base && size == rhs.size; + } + + bool + operator != (const Range &rhs) const + { + return base != rhs.base || size != rhs.size; + } + }; + + //---------------------------------------------------------------------- + // A range array class where you get to define the type of the ranges + // that the collection contains. + //---------------------------------------------------------------------- + + template + class RangeArray + { + public: + typedef B BaseType; + typedef S SizeType; + typedef Range Entry; + typedef llvm::SmallVector Collection; + + RangeArray () : + m_entries () + { + } + + ~RangeArray() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + bool + RemoveEntrtAtIndex (uint32_t idx) + { + if (idx < m_entries.size()) + { + m_entries.erase (m_entries.begin() + idx); + return true; + } + return false; + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + void + CombineConsecutiveRanges () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + // Can't combine if ranges if we have zero or one range + if (m_entries.size() > 1) + { + // The list should be sorted prior to calling this function + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + minimal_ranges.back().SetRangeEnd (std::max(prev->GetRangeEnd(), pos->GetRangeEnd())); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + } + + + BaseType + GetMinRangeBase (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // first range's base + return m_entries.front().GetRangeBase(); + } + + BaseType + GetMaxRangeEnd (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // last range's end + return m_entries.back().GetRangeEnd(); + } + + void + Slide (BaseType slide) + { + typename Collection::iterator pos, end; + for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) + pos->Slide (slide); + } + + void + Clear () + { + m_entries.clear(); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + Entry entry (addr, 1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + protected: + Collection m_entries; + }; + + template + class RangeVector + { + public: + typedef B BaseType; + typedef S SizeType; + typedef Range Entry; + typedef std::vector Collection; + + RangeVector () : + m_entries () + { + } + + ~RangeVector() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + bool + RemoveEntrtAtIndex (uint32_t idx) + { + if (idx < m_entries.size()) + { + m_entries.erase (m_entries.begin() + idx); + return true; + } + return false; + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + void + CombineConsecutiveRanges () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + // Can't combine if ranges if we have zero or one range + if (m_entries.size() > 1) + { + // The list should be sorted prior to calling this function + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->Overlap(*pos)) + minimal_ranges.back().SetRangeEnd (std::max(prev->GetRangeEnd(), pos->GetRangeEnd())); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + } + + + BaseType + GetMinRangeBase (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // first range's base + return m_entries.front().GetRangeBase(); + } + + BaseType + GetMaxRangeEnd (BaseType fail_value) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (m_entries.empty()) + return fail_value; + // m_entries must be sorted, so if we aren't empty, we grab the + // last range's end + return m_entries.back().GetRangeEnd(); + } + + void + Slide (BaseType slide) + { + typename Collection::iterator pos, end; + for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) + pos->Slide (slide); + } + + void + Clear () + { + m_entries.clear(); + } + + void + Reserve (typename Collection::size_type size) + { + m_entries.resize (size); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + Entry entry (addr, 1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if (!m_entries.empty()) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + protected: + Collection m_entries; + }; + + //---------------------------------------------------------------------- + // A simple range with data class where you get to define the type of + // the range base "B", the type used for the range byte size "S", and + // the type for the associated data "T". + //---------------------------------------------------------------------- + template + struct RangeData : public Range + { + typedef T DataType; + + DataType data; + + RangeData () : + Range (), + data () + { + } + + RangeData (B base, S size) : + Range (base, size), + data () + { + } + + RangeData (B base, S size, DataType d) : + Range (base, size), + data (d) + { + } + + bool + operator < (const RangeData &rhs) const + { + if (this->base == rhs.base) + { + if (this->size == rhs.size) + return this->data < rhs.data; + else + return this->size < rhs.size; + } + return this->base < rhs.base; + } + + bool + operator == (const RangeData &rhs) const + { + return this->GetRangeBase() == rhs.GetRangeBase() && + this->GetByteSize() == rhs.GetByteSize() && + this->data == rhs.data; + } + + bool + operator != (const RangeData &rhs) const + { + return this->GetRangeBase() != rhs.GetRangeBase() || + this->GetByteSize() != rhs.GetByteSize() || + this->data != rhs.data; + } + }; + + template + class RangeDataArray + { + public: + typedef RangeData Entry; + typedef llvm::SmallVector Collection; + + + RangeDataArray () + { + } + + ~RangeDataArray() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + + void + CombineConsecutiveEntriesWithEqualData () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + minimal_ranges.back().SetRangeEnd (pos->GetRangeEnd()); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + + void + Clear () + { + m_entries.clear(); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + Entry * + FindEntryThatContains (B addr) + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::iterator begin = m_entries.begin(); + typename Collection::iterator end = m_entries.end(); + typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + Entry * + Back() + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + const Entry * + Back() const + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + protected: + Collection m_entries; + }; + + // Same as RangeDataArray, but uses std::vector as to not + // require static storage of N items in the class itself + template + class RangeDataVector + { + public: + typedef RangeData Entry; + typedef std::vector Collection; + + RangeDataVector () + { + } + + ~RangeDataVector() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + + void + CombineConsecutiveEntriesWithEqualData () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator prev; + bool can_combine = false; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + { + can_combine = true; + break; + } + } + + // We we can combine at least one entry, then we make a new collection + // and populate it accordingly, and then swap it into place. + if (can_combine) + { + Collection minimal_ranges; + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && prev->data == pos->data) + minimal_ranges.back().SetRangeEnd (pos->GetRangeEnd()); + else + minimal_ranges.push_back (*pos); + } + // Use the swap technique in case our new vector is much smaller. + // We must swap when using the STL because std::vector objects never + // release or reduce the memory once it has been allocated/reserved. + m_entries.swap (minimal_ranges); + } + } + + // Calculate the byte size of ranges with zero byte sizes by finding + // the next entry with a base address > the current base address + void + CalculateSizesOfZeroByteSizeRanges () + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + typename Collection::iterator pos; + typename Collection::iterator end; + typename Collection::iterator next; + for (pos = m_entries.begin(), end = m_entries.end(); pos != end; ++pos) + { + if (pos->GetByteSize() == 0) + { + // Watch out for multiple entries with same address and make sure + // we find an entry that is greater than the current base address + // before we use that for the size + auto curr_base = pos->GetRangeBase(); + for (next = pos + 1; next != end; ++next) + { + auto next_base = next->GetRangeBase(); + if (next_base > curr_base) + { + pos->SetByteSize (next_base - curr_base); + break; + } + } + } + } + + } + + void + Clear () + { + m_entries.clear(); + } + + void + Reserve (typename Collection::size_type size) + { + m_entries.resize (size); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iContains(addr)) + { + return std::distance (begin, pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + return std::distance (begin, pos); + } + } + return UINT32_MAX; + } + + Entry * + FindEntryThatContains (B addr) + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::iterator begin = m_entries.begin(); + typename Collection::iterator end = m_entries.end(); + typename Collection::iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + const Entry * + FindEntryThatContains (B addr) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + Entry entry; + entry.SetRangeBase(addr); + entry.SetByteSize(1); + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, entry, BaseLessThan); + + if (pos != end && pos->Contains(addr)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(addr)) + { + return &(*pos); + } + } + } + return NULL; + } + + const Entry * + FindEntryThatContains (const Entry &range) const + { +#ifdef ASSERT_RANGEMAP_ARE_SORTED + assert (IsSorted()); +#endif + if ( !m_entries.empty() ) + { + typename Collection::const_iterator begin = m_entries.begin(); + typename Collection::const_iterator end = m_entries.end(); + typename Collection::const_iterator pos = std::lower_bound (begin, end, range, BaseLessThan); + + if (pos != end && pos->Contains(range)) + { + return &(*pos); + } + else if (pos != begin) + { + --pos; + if (pos->Contains(range)) + { + return &(*pos); + } + } + } + return NULL; + } + + Entry * + Back() + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + const Entry * + Back() const + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + protected: + Collection m_entries; + }; + + + //---------------------------------------------------------------------- + // A simple range with data class where you get to define the type of + // the range base "B", the type used for the range byte size "S", and + // the type for the associated data "T". + //---------------------------------------------------------------------- + template + struct AddressData + { + typedef B BaseType; + typedef T DataType; + + BaseType addr; + DataType data; + + AddressData () : + addr (), + data () + { + } + + AddressData (B a, DataType d) : + addr (a), + data (d) + { + } + + bool + operator < (const AddressData &rhs) const + { + if (this->addr == rhs.addr) + return this->data < rhs.data; + return this->addr < rhs.addr; + } + + bool + operator == (const AddressData &rhs) const + { + return this->addr == rhs.addr && + this->data == rhs.data; + } + + bool + operator != (const AddressData &rhs) const + { + return this->addr != rhs.addr || + this->data == rhs.data; + } + }; + + + template + class AddressDataArray + { + public: + typedef AddressData Entry; + typedef llvm::SmallVector Collection; + + + AddressDataArray () + { + } + + ~AddressDataArray() + { + } + + void + Append (const Entry &entry) + { + m_entries.push_back (entry); + } + + void + Sort () + { + if (m_entries.size() > 1) + std::stable_sort (m_entries.begin(), m_entries.end()); + } + +#ifdef ASSERT_RANGEMAP_ARE_SORTED + bool + IsSorted () const + { + typename Collection::const_iterator pos, end, prev; + // First we determine if we can combine any of the Entry objects so we + // don't end up allocating and making a new collection for no reason + for (pos = m_entries.begin(), end = m_entries.end(), prev = end; pos != end; prev = pos++) + { + if (prev != end && *pos < *prev) + return false; + } + return true; + } +#endif + + void + Clear () + { + m_entries.clear(); + } + + bool + IsEmpty () const + { + return m_entries.empty(); + } + + size_t + GetSize () const + { + return m_entries.size(); + } + + const Entry * + GetEntryAtIndex (size_t i) const + { + if (iaddr == addr || !exact_match_only) + return &(*pos); + } + } + return NULL; + } + + const Entry * + FindNextEntry (const Entry *entry) + { + if (entry >= &*m_entries.begin() && entry + 1 < &*m_entries.end()) + return entry + 1; + return NULL; + } + + Entry * + Back() + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + const Entry * + Back() const + { + if (!m_entries.empty()) + return &m_entries.back(); + return NULL; + } + + protected: + Collection m_entries; + }; + +} // namespace lldb_private + +#endif // liblldb_RangeMap_h_ diff --git a/include/lldb/Core/RegisterValue.h b/include/lldb/Core/RegisterValue.h new file mode 100644 index 00000000000..cf29cea46d3 --- /dev/null +++ b/include/lldb/Core/RegisterValue.h @@ -0,0 +1,406 @@ +//===-- RegisterValue.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_RegisterValue_h +#define lldb_RegisterValue_h + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-private.h" +#include "lldb/Host/Endian.h" + +//#define ENABLE_128_BIT_SUPPORT 1 +namespace lldb_private { + + class RegisterValue + { + public: + enum + { + kMaxRegisterByteSize = 32u + }; + enum Type + { + eTypeInvalid, + eTypeUInt8, + eTypeUInt16, + eTypeUInt32, + eTypeUInt64, +#if defined (ENABLE_128_BIT_SUPPORT) + eTypeUInt128, +#endif + eTypeFloat, + eTypeDouble, + eTypeLongDouble, + eTypeBytes + }; + + RegisterValue () : + m_type (eTypeInvalid) + { + } + + explicit + RegisterValue (uint8_t inst) : + m_type (eTypeUInt8) + { + m_data.uint8 = inst; + } + + explicit + RegisterValue (uint16_t inst) : + m_type (eTypeUInt16) + { + m_data.uint16 = inst; + } + + explicit + RegisterValue (uint32_t inst) : + m_type (eTypeUInt32) + { + m_data.uint32 = inst; + } + + explicit + RegisterValue (uint64_t inst) : + m_type (eTypeUInt64) + { + m_data.uint64 = inst; + } + +#if defined (ENABLE_128_BIT_SUPPORT) + explicit + RegisterValue (__uint128_t inst) : + m_type (eTypeUInt128) + { + m_data.uint128 = inst; + } +#endif + explicit + RegisterValue (float value) : + m_type (eTypeFloat) + { + m_data.ieee_float = value; + } + + explicit + RegisterValue (double value) : + m_type (eTypeDouble) + { + m_data.ieee_double = value; + } + + explicit + RegisterValue (long double value) : + m_type (eTypeLongDouble) + { + m_data.ieee_long_double = value; + } + + explicit + RegisterValue (uint8_t *bytes, size_t length, lldb::ByteOrder byte_order) + { + SetBytes (bytes, length, byte_order); + } + + RegisterValue::Type + GetType () const + { + return m_type; + } + + bool + CopyValue (const RegisterValue &rhs); + + void + SetType (RegisterValue::Type type) + { + m_type = type; + } + + RegisterValue::Type + SetType (const RegisterInfo *reg_info); + + bool + GetData (DataExtractor &data) const; + + // Copy the register value from this object into a buffer in "dst" + // and obey the "dst_byte_order" when copying the data. Also watch out + // in case "dst_len" is longer or shorter than the register value + // described by "reg_info" and only copy the least significant bytes + // of the register value, or pad the destination with zeroes if the + // register byte size is shorter that "dst_len" (all while correctly + // abiding the "dst_byte_order"). Returns the number of bytes copied + // into "dst". + uint32_t + GetAsMemoryData (const RegisterInfo *reg_info, + void *dst, + uint32_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const; + + uint32_t + SetFromMemoryData (const RegisterInfo *reg_info, + const void *src, + uint32_t src_len, + lldb::ByteOrder src_byte_order, + Error &error); + + bool + GetScalarValue (Scalar &scalar) const; + + uint8_t + GetAsUInt8 (uint8_t fail_value = UINT8_MAX, bool *success_ptr = NULL) const + { + if (m_type == eTypeUInt8) + { + if (success_ptr) + *success_ptr = true; + return m_data.uint8; + } + if (success_ptr) + *success_ptr = true; + return fail_value; + } + + uint16_t + GetAsUInt16 (uint16_t fail_value = UINT16_MAX, bool *success_ptr = NULL) const; + + uint32_t + GetAsUInt32 (uint32_t fail_value = UINT32_MAX, bool *success_ptr = NULL) const; + + uint64_t + GetAsUInt64 (uint64_t fail_value = UINT64_MAX, bool *success_ptr = NULL) const; + +#if defined (ENABLE_128_BIT_SUPPORT) + __uint128_t + GetAsUInt128 (__uint128_t fail_value = ~((__uint128_t)0), bool *success_ptr = NULL) const; +#endif + + float + GetAsFloat (float fail_value = 0.0f, bool *success_ptr = NULL) const; + + double + GetAsDouble (double fail_value = 0.0, bool *success_ptr = NULL) const; + + long double + GetAsLongDouble (long double fail_value = 0.0, bool *success_ptr = NULL) const; + + void + SetValueToInvalid () + { + m_type = eTypeInvalid; + } + + bool + ClearBit (uint32_t bit); + + bool + SetBit (uint32_t bit); + + bool + operator == (const RegisterValue &rhs) const; + + bool + operator != (const RegisterValue &rhs) const; + + void + operator = (uint8_t uint) + { + m_type = eTypeUInt8; + m_data.uint8 = uint; + } + + void + operator = (uint16_t uint) + { + m_type = eTypeUInt16; + m_data.uint16 = uint; + } + + void + operator = (uint32_t uint) + { + m_type = eTypeUInt32; + m_data.uint32 = uint; + } + + void + operator = (uint64_t uint) + { + m_type = eTypeUInt64; + m_data.uint64 = uint; + } + +#if defined (ENABLE_128_BIT_SUPPORT) + void + operator = (__uint128_t uint) + { + m_type = eTypeUInt128; + m_data.uint128 = uint; + } +#endif + void + operator = (float f) + { + m_type = eTypeFloat; + m_data.ieee_float = f; + } + + void + operator = (double f) + { + m_type = eTypeDouble; + m_data.ieee_double = f; + } + + void + operator = (long double f) + { + m_type = eTypeLongDouble; + m_data.ieee_long_double = f; + } + + void + SetUInt8 (uint8_t uint) + { + m_type = eTypeUInt8; + m_data.uint8 = uint; + } + + void + SetUInt16 (uint16_t uint) + { + m_type = eTypeUInt16; + m_data.uint16 = uint; + } + + void + SetUInt32 (uint32_t uint, Type t = eTypeUInt32) + { + m_type = t; + m_data.uint32 = uint; + } + + void + SetUInt64 (uint64_t uint, Type t = eTypeUInt64) + { + m_type = t; + m_data.uint64 = uint; + } + +#if defined (ENABLE_128_BIT_SUPPORT) + void + SetUInt128 (__uint128_t uint) + { + m_type = eTypeUInt128; + m_data.uint128 = uint; + } +#endif + bool + SetUInt (uint64_t uint, uint32_t byte_size); + + void + SetFloat (float f) + { + m_type = eTypeFloat; + m_data.ieee_float = f; + } + + void + SetDouble (double f) + { + m_type = eTypeDouble; + m_data.ieee_double = f; + } + + void + SetLongDouble (long double f) + { + m_type = eTypeLongDouble; + m_data.ieee_long_double = f; + } + + void + SetBytes (const void *bytes, size_t length, lldb::ByteOrder byte_order); + + bool + SignExtend (uint32_t sign_bitpos); + + Error + SetValueFromCString (const RegisterInfo *reg_info, + const char *value_str); + + Error + SetValueFromData (const RegisterInfo *reg_info, + DataExtractor &data, + lldb::offset_t offset, + bool partial_data_ok); + + // The default value of 0 for reg_name_right_align_at means no alignment at all. + bool + Dump (Stream *s, + const RegisterInfo *reg_info, + bool prefix_with_name, + bool prefix_with_alt_name, + lldb::Format format, + uint32_t reg_name_right_align_at = 0) const; + + void * + GetBytes (); + + const void * + GetBytes () const; + + lldb::ByteOrder + GetByteOrder () const + { + if (m_type == eTypeBytes) + return m_data.buffer.byte_order; + return lldb::endian::InlHostByteOrder(); + } + + uint32_t + GetByteSize () const; + + void + Clear(); + + protected: + + RegisterValue::Type m_type; + union + { + uint8_t uint8; + uint16_t uint16; + uint32_t uint32; + uint64_t uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + __uint128_t uint128; +#endif + float ieee_float; + double ieee_double; + long double ieee_long_double; + struct + { + uint8_t bytes[kMaxRegisterByteSize]; // This must be big enough to hold any register for any supported target. + uint8_t length; + lldb::ByteOrder byte_order; + } buffer; + } m_data; + }; + +} // namespace lldb_private + +#endif // lldb_RegisterValue_h diff --git a/include/lldb/Core/RegularExpression.h b/include/lldb/Core/RegularExpression.h new file mode 100644 index 00000000000..eeeb914bfa9 --- /dev/null +++ b/include/lldb/Core/RegularExpression.h @@ -0,0 +1,253 @@ +//===-- RegularExpression.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DBRegex_h_ +#define liblldb_DBRegex_h_ +#if defined(__cplusplus) + +#include +#include + +#include +#include + +namespace llvm +{ + class StringRef; +} + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class RegularExpression RegularExpression.h "lldb/Core/RegularExpression.h" +/// @brief A C++ wrapper class for regex. +/// +/// This regular expression class wraps the posix regex functions +/// \c regcomp(), \c regerror(), \c regexec(), and \c regfree() from +/// the header file in \c /usr/include/regex\.h. +//---------------------------------------------------------------------- +class RegularExpression +{ +public: + class Match + { + public: + Match (uint32_t max_matches) : + m_matches () + { + if (max_matches > 0) + m_matches.resize(max_matches + 1); + } + + void + Clear() + { + const size_t num_matches = m_matches.size(); + regmatch_t invalid_match = { -1, -1 }; + for (size_t i=0; i m_matches; ///< Where parenthesized subexpressions results are stored + }; + //------------------------------------------------------------------ + /// Default constructor. + /// + /// The default constructor that initializes the object state such + /// that it contains no compiled regular expression. + //------------------------------------------------------------------ + RegularExpression (); + + //------------------------------------------------------------------ + /// Constructor that takes a regulare expression with flags. + /// + /// Constructor that compiles \a re using \a flags and stores the + /// resulting compiled regular expression into this object. + /// + /// @param[in] re + /// A c string that represents the regular expression to + /// compile. + /// + /// @param[in] flags + /// Flags that are passed the the \c regcomp() function. + //------------------------------------------------------------------ + explicit + RegularExpression (const char* re, int flags); + + // This one uses flags = REG_EXTENDED. + explicit + RegularExpression (const char* re); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Any previosuly compiled regular expression contained in this + /// object will be freed. + //------------------------------------------------------------------ + ~RegularExpression (); + + RegularExpression (const RegularExpression &rhs); + + const RegularExpression & operator=(const RegularExpression &rhs); + + //------------------------------------------------------------------ + /// Compile a regular expression. + /// + /// Compile a regular expression using the supplied regular + /// expression text and flags. The compied 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 previosuly compiled regular + /// expression contained in this object will be freed. + /// + /// @param[in] re + /// A NULL terminated C string that represents the regular + /// expression to compile. + /// + /// @param[in] flags + /// Flags that are passed the the \c regcomp() function. + /// + /// @return + /// \b true if the regular expression compiles successfully, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + Compile (const char* re); + + bool + Compile (const char* re, int flags); + + //------------------------------------------------------------------ + /// Executes a regular expression. + /// + /// Execute a regular expression match using the compiled regular + /// expression that is already in this object against the match + /// string \a s. If any parens are used for regular expression + /// matches \a match_count should indicate the number of regmatch_t + /// values that are present in \a match_ptr. The regular expression + /// will be executed using the \a execute_flags + /// + /// @param[in] string + /// The string to match against the compile regular expression. + /// + /// @param[in] match + /// A pointer to a RegularExpression::Match structure that was + /// properly initialized with the desired number of maximum + /// matches, or NULL if no parenthesized matching is needed. + /// + /// @param[in] execute_flags + /// Flags to pass to the \c regexec() function. + /// + /// @return + /// \b true if \a string matches the compiled regular + /// expression, \b false otherwise. + //------------------------------------------------------------------ + bool + Execute (const char* string, Match *match = NULL, int execute_flags = 0) const; + + size_t + GetErrorAsCString (char *err_str, size_t err_str_max_len) const; + + //------------------------------------------------------------------ + /// Free the compiled regular expression. + /// + /// If this object contains a valid compiled regular expression, + /// this function will free any resources it was consuming. + //------------------------------------------------------------------ + void + Free (); + + //------------------------------------------------------------------ + /// Access the regular expression text. + /// + /// Returns the text that was used to compile the current regular + /// expression. + /// + /// @return + /// The NULL terminated C string that was used to compile the + /// current regular expression + //------------------------------------------------------------------ + const char* + GetText () const; + + int + GetCompileFlags () const + { + return m_compile_flags; + } + + //------------------------------------------------------------------ + /// Test if valid. + /// + /// Test if this object contains a valid regular expression. + /// + /// @return + /// \b true if the regular expression compiled and is ready + /// for execution, \b false otherwise. + //------------------------------------------------------------------ + bool + IsValid () const; + + void + Clear () + { + Free(); + m_re.clear(); + m_compile_flags = 0; + m_comp_err = 1; + } + + int + GetErrorCode() const + { + return m_comp_err; + } + + bool + operator < (const RegularExpression& rhs) const; + +private: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + std::string m_re; ///< A copy of the original regular expression text + int m_comp_err; ///< Error code for the regular expression compilation + regex_t m_preg; ///< The compiled regular expression + int m_compile_flags; ///< Stores the flags from the last compile. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_DBRegex_h_ diff --git a/include/lldb/Core/STLUtils.h b/include/lldb/Core/STLUtils.h new file mode 100644 index 00000000000..9321e057a39 --- /dev/null +++ b/include/lldb/Core/STLUtils.h @@ -0,0 +1,94 @@ +//===-- STLUtils.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_STLUtils_h_ +#define liblldb_STLUtils_h_ +#if defined(__cplusplus) + +#include + +#include +#include +#include + +//---------------------------------------------------------------------- +// C string less than compare function object +//---------------------------------------------------------------------- +struct CStringCompareFunctionObject +{ + bool operator() (const char* s1, const char* s2) const + { + return strcmp(s1, s2) < 0; + } +}; + + +//---------------------------------------------------------------------- +// C string equality function object (binary predicate). +//---------------------------------------------------------------------- +struct CStringEqualBinaryPredicate +{ + bool operator()(const char* s1, const char* s2) const + { + return strcmp(s1, s2) == 0; + } +}; + + +//---------------------------------------------------------------------- +// Templated type for finding an entry in a std::map whose value +// is equal to something +//---------------------------------------------------------------------- +template +class ValueEquals +{ +private: + S second_value; + +public: + ValueEquals (const S& val) : second_value(val) + {} + // Compare the second item + bool operator() (std::pair elem) + { + return elem.second == second_value; + } +}; + +template +inline void PrintAllCollectionElements (std::ostream &s, const T& coll, const char* header_cstr=NULL, const char* separator_cstr=" ") +{ + typename T::const_iterator pos; + + if (header_cstr) + s << header_cstr; + for (pos=coll.begin(); pos!=coll.end(); ++pos) { + s << *pos << separator_cstr; + } + s << std::endl; +} + +// The function object below can be used to delete a STL container that +// contains C++ object pointers. +// +// Usage: std::for_each(vector.begin(), vector.end(), for_each_delete()); + +struct for_each_cplusplus_delete +{ + template + void operator()(T *ptr){ delete ptr;} +}; + +typedef std::vector STLStringArray; +typedef std::vector CStringArray; + + + +#endif // #if defined(__cplusplus) +#endif // liblldb_STLUtils_h_ diff --git a/include/lldb/Core/Scalar.h b/include/lldb/Core/Scalar.h new file mode 100644 index 00000000000..821a0fb1ae2 --- /dev/null +++ b/include/lldb/Core/Scalar.h @@ -0,0 +1,341 @@ +//===-- Scalar.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Scalar_h_ +#define liblldb_Scalar_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A class designed to hold onto values and their corresponding types. +// Operators are defined and Scalar objects will correctly promote +// their types and values before performing these operations. Type +// promotion currently follows the ANSI C type promotion rules. +//---------------------------------------------------------------------- +class Scalar +{ +public: + enum Type + { + e_void = 0, + e_sint, + e_uint, + e_slong, + e_ulong, + e_slonglong, + e_ulonglong, + e_float, + e_double, + e_long_double + }; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Scalar(); + Scalar(int v) : m_type(e_sint), m_data() { m_data.sint = v; } + Scalar(unsigned int v) : m_type(e_uint), m_data() { m_data.uint = v; } + Scalar(long v) : m_type(e_slong), m_data() { m_data.slong = v; } + Scalar(unsigned long v) : m_type(e_ulong), m_data() { m_data.ulong = v; } + Scalar(long long v) : m_type(e_slonglong), m_data() { m_data.slonglong = v; } + Scalar(unsigned long long v): m_type(e_ulonglong), m_data() { m_data.ulonglong = v; } + Scalar(float v) : m_type(e_float), m_data() { m_data.flt = v; } + Scalar(double v) : m_type(e_double), m_data() { m_data.dbl = v; } + Scalar(long double v) : m_type(e_long_double), m_data() { m_data.ldbl = v; } + Scalar(const Scalar& rhs); + //Scalar(const RegisterValue& reg_value); + virtual ~Scalar(); + + bool + SignExtend (uint32_t bit_pos); + + bool + ExtractBitfield (uint32_t bit_size, + uint32_t bit_offset); + + size_t + GetByteSize() const; + + static size_t + GetMaxByteSize() + { + return sizeof(ValueData); + } + + bool + GetData (DataExtractor &data, size_t limit_byte_size = UINT32_MAX) const; + + size_t + GetAsMemoryData (void *dst, + size_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &error) const; + + bool + IsZero() const; + + void + Clear() { m_type = e_void; m_data.ulonglong = 0; } + + const char * + GetTypeAsCString() const; + + void + GetValue (Stream *s, bool show_type) const; + + bool + IsValid() const + { + return (m_type >= e_sint) && (m_type <= e_long_double); + } + + bool + Promote(Scalar::Type type); + + bool + Cast (Scalar::Type type); + + bool + MakeSigned (); + + static const char * + GetValueTypeAsCString (Scalar::Type value_type); + + static Scalar::Type + GetValueTypeForSignedIntegerWithByteSize (size_t byte_size); + + static Scalar::Type + GetValueTypeForUnsignedIntegerWithByteSize (size_t byte_size); + + static Scalar::Type + GetValueTypeForFloatWithByteSize (size_t byte_size); + + //---------------------------------------------------------------------- + // All operators can benefits from the implicit conversions that will + // happen automagically by the compiler, so no temporary objects will + // need to be created. As a result, we currently don't need a variety of + // overloaded set value accessors. + //---------------------------------------------------------------------- + Scalar& operator= (const int i); + Scalar& operator= (unsigned int v); + Scalar& operator= (long v); + Scalar& operator= (unsigned long v); + Scalar& operator= (long long v); + Scalar& operator= (unsigned long long v); + Scalar& operator= (float v); + Scalar& operator= (double v); + Scalar& operator= (long double v); + Scalar& operator= (const Scalar& rhs); // Assignment operator + Scalar& operator+= (const Scalar& rhs); + Scalar& operator<<= (const Scalar& rhs); // Shift left + Scalar& operator>>= (const Scalar& rhs); // Shift right (arithmetic) + Scalar& operator&= (const Scalar& rhs); + + //---------------------------------------------------------------------- + // Shifts the current value to the right without maintaining the current + // sign of the value (if it is signed). + //---------------------------------------------------------------------- + bool + ShiftRightLogical(const Scalar& rhs); // Returns true on success + + //---------------------------------------------------------------------- + // Takes the absolute value of the current value if it is signed, else + // the value remains unchanged. + // Returns false if the contained value has a void type. + //---------------------------------------------------------------------- + bool + AbsoluteValue(); // Returns true on success + //---------------------------------------------------------------------- + // Negates the current value (even for unsigned values). + // Returns false if the contained value has a void type. + //---------------------------------------------------------------------- + bool + UnaryNegate(); // Returns true on success + //---------------------------------------------------------------------- + // Inverts all bits in the current value as long as it isn't void or + // a float/double/long double type. + // Returns false if the contained value has a void/float/double/long + // double type, else the value is inverted and true is returned. + //---------------------------------------------------------------------- + bool + OnesComplement(); // Returns true on success + + //---------------------------------------------------------------------- + // Access the type of the current value. + //---------------------------------------------------------------------- + Scalar::Type + GetType() const { return m_type; } + + //---------------------------------------------------------------------- + // Returns a casted value of the current contained data without + // modifying the current value. FAIL_VALUE will be returned if the type + // of the value is void or invalid. + //---------------------------------------------------------------------- + int + SInt(int fail_value = 0) const; + + // Return the raw unsigned integer without any casting or conversion + unsigned int + RawUInt () const; + + // Return the raw unsigned long without any casting or conversion + unsigned long + RawULong () const; + + // Return the raw unsigned long long without any casting or conversion + unsigned long long + RawULongLong () const; + + unsigned int + UInt(unsigned int fail_value = 0) const; + + long + SLong(long fail_value = 0) const; + + unsigned long + ULong(unsigned long fail_value = 0) const; + + long long + SLongLong(long long fail_value = 0) const; + + unsigned long long + ULongLong(unsigned long long fail_value = 0) const; + + float + Float(float fail_value = 0.0f) const; + + double + Double(double fail_value = 0.0) const; + + long double + LongDouble(long double fail_value = 0.0) const; + + uint64_t + GetRawBits64 (uint64_t fail_value) const; + + Error + SetValueFromCString (const char *s, lldb::Encoding encoding, size_t byte_size); + + Error + SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t byte_size); + + static 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; + } + + static 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: + typedef int sint_t; + typedef unsigned int uint_t; + typedef long slong_t; + typedef unsigned long ulong_t; + typedef long long slonglong_t; + typedef unsigned long long ulonglong_t; + typedef float float_t; + typedef double double_t; + typedef long double long_double_t; + + union ValueData + { + int sint; + unsigned int uint; + long slong; + unsigned long ulong; + long long slonglong; + unsigned long long ulonglong; + float flt; + double dbl; + long double ldbl; + }; + + //------------------------------------------------------------------ + // Classes that inherit from Scalar can see and modify these + //------------------------------------------------------------------ + Scalar::Type m_type; + ValueData m_data; + +private: + friend const Scalar operator+ (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator- (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator/ (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator* (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator& (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator| (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator% (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator^ (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator<< (const Scalar& lhs, const Scalar& rhs); + friend const Scalar operator>> (const Scalar& lhs, const Scalar& rhs); + friend bool operator== (const Scalar& lhs, const Scalar& rhs); + friend bool operator!= (const Scalar& lhs, const Scalar& rhs); + friend bool operator< (const Scalar& lhs, const Scalar& rhs); + friend bool operator<= (const Scalar& lhs, const Scalar& rhs); + friend bool operator> (const Scalar& lhs, const Scalar& rhs); + friend bool operator>= (const Scalar& lhs, const Scalar& rhs); + +}; + +//---------------------------------------------------------------------- +// Split out the operators into a format where the compiler will be able +// to implicitly convert numbers into Scalar objects. +// +// This allows code like: +// Scalar two(2); +// Scalar four = two * 2; +// Scalar eight = 2 * four; // This would cause an error if the +// // operator* was implemented as a +// // member function. +// SEE: +// Item 19 of "Effective C++ Second Edition" by Scott Meyers +// Differentiate among members functions, non-member functions, and +// friend functions +//---------------------------------------------------------------------- +const Scalar operator+ (const Scalar& lhs, const Scalar& rhs); +const Scalar operator- (const Scalar& lhs, const Scalar& rhs); +const Scalar operator/ (const Scalar& lhs, const Scalar& rhs); +const Scalar operator* (const Scalar& lhs, const Scalar& rhs); +const Scalar operator& (const Scalar& lhs, const Scalar& rhs); +const Scalar operator| (const Scalar& lhs, const Scalar& rhs); +const Scalar operator% (const Scalar& lhs, const Scalar& rhs); +const Scalar operator^ (const Scalar& lhs, const Scalar& rhs); +const Scalar operator<< (const Scalar& lhs, const Scalar& rhs); +const Scalar operator>> (const Scalar& lhs, const Scalar& rhs); +bool operator== (const Scalar& lhs, const Scalar& rhs); +bool operator!= (const Scalar& lhs, const Scalar& rhs); +bool operator< (const Scalar& lhs, const Scalar& rhs); +bool operator<= (const Scalar& lhs, const Scalar& rhs); +bool operator> (const Scalar& lhs, const Scalar& rhs); +bool operator>= (const Scalar& lhs, const Scalar& rhs); + +} // namespace lldb_private + +#endif // liblldb_Scalar_h_ diff --git a/include/lldb/Core/SearchFilter.h b/include/lldb/Core/SearchFilter.h new file mode 100644 index 00000000000..57f1b9c1a0a --- /dev/null +++ b/include/lldb/Core/SearchFilter.h @@ -0,0 +1,447 @@ +//===-- SearchFilter.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SearchFilter_h_ +#define liblldb_SearchFilter_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/FileSpecList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Searcher SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief Class that is driven by the SearchFilter to search the +/// SymbolContext space of the target program. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// Provides the callback and search depth for the SearchFilter search. +//---------------------------------------------------------------------- + +class Searcher +{ +public: + typedef enum { + eCallbackReturnStop = 0, // Stop the iteration + eCallbackReturnContinue, // Continue the iteration + eCallbackReturnPop // Pop one level up and continue iterating + } CallbackReturn; + + typedef enum { + eDepthTarget, + eDepthModule, + eDepthCompUnit, + eDepthFunction, + eDepthBlock, + eDepthAddress + } Depth; + + Searcher (); + + virtual ~Searcher (); + + virtual CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete) = 0; + + virtual Depth + GetDepth () = 0; + + //------------------------------------------------------------------ + /// Prints a canonical description for the searcher to the stream \a s. + /// + /// @param[in] s + /// Stream to which the output is copied. + //------------------------------------------------------------------ + virtual void + GetDescription(Stream *s); +}; + +//---------------------------------------------------------------------- +/// @class SearchFilter SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief Class descends through the SymbolContext space of the target, +/// applying a filter at each stage till it reaches the depth specified by +/// the GetDepth method of the searcher, and calls its callback at that point. +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +/// General Outline: +/// Provides the callback and search depth for the SearchFilter search. +/// +/// The search is done by cooperation between the search filter and the searcher. +/// The search filter does the heavy work of recursing through the SymbolContext +/// space of the target program's symbol space. The Searcher specifies the depth +/// at which it wants its callback to be invoked. Note that since the resolution +/// of the Searcher may be greater than that of the SearchFilter, before the +/// Searcher qualifies an address it should pass it to "AddressPasses." +/// The default implementation is "Everything Passes." +//---------------------------------------------------------------------- + +class SearchFilter +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search. + /// + /// @param[in] target + /// The Target that provides the module list to search. + //------------------------------------------------------------------ + SearchFilter (const lldb::TargetSP &target_sp); + + SearchFilter (const SearchFilter& rhs); + + virtual + ~SearchFilter (); + + const SearchFilter& + operator=(const SearchFilter& rhs); + + //------------------------------------------------------------------ + /// Call this method with a file spec to see if that spec passes the filter. + /// + /// @param[in] spec + /// The file spec to check against the filter. + /// @return + /// \b true if \a spec passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ModulePasses (const FileSpec &spec); + + //------------------------------------------------------------------ + /// Call this method with a Module to see if that module passes the filter. + /// + /// @param[in] module + /// The Module to check against the filter. + /// + /// @return + /// \b true if \a module passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); + + //------------------------------------------------------------------ + /// Call this method with a Address to see if \a address passes the filter. + /// + /// @param[in] addr + /// The address to check against the filter. + /// + /// @return + /// \b true if \a address passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + AddressPasses (Address &addr); + + //------------------------------------------------------------------ + /// Call this method with a FileSpec to see if \a file spec passes the filter + /// as the name of a compilation unit. + /// + /// @param[in] fileSpec + /// The file spec to check against the filter. + /// + /// @return + /// \b true if \a file spec passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + //------------------------------------------------------------------ + /// Call this method with a CompileUnit to see if \a comp unit passes the filter. + /// + /// @param[in] compUnit + /// The CompileUnit to check against the filter. + /// + /// @return + /// \b true if \a Comp Unit passes, and \b false otherwise. + //------------------------------------------------------------------ + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + //------------------------------------------------------------------ + /// Call this method to do the search using the Searcher. + /// + /// @param[in] searcher + /// The searcher to drive with this search. + /// + //------------------------------------------------------------------ + virtual void + Search (Searcher &searcher); + + //------------------------------------------------------------------ + /// Call this method to do the search using the Searcher in the module list + /// \a modules. + /// + /// @param[in] searcher + /// The searcher to drive with this search. + /// + /// @param[in] modules + /// The module list within which to restrict the search. + /// + //------------------------------------------------------------------ + virtual void + SearchInModuleList (Searcher &searcher, ModuleList &modules); + + //------------------------------------------------------------------ + /// This determines which items are REQUIRED for the filter to pass. + /// For instance, if you are filtering by Compilation Unit, obviously + /// symbols that have no compilation unit can't pass So return eSymbolContextCU + /// and search callbacks can then short cut the search to avoid looking at + /// things that obviously won't pass. + /// + /// @return + /// The required elements for the search, which is an or'ed together + /// set of lldb:SearchContextItem enum's. + /// + //------------------------------------------------------------------ + virtual uint32_t + GetFilterRequiredItems (); + + //------------------------------------------------------------------ + /// Prints a canonical description for the search filter to the stream \a s. + /// + /// @param[in] s + /// Stream to which the output is copied. + //------------------------------------------------------------------ + virtual void + GetDescription(Stream *s); + + //------------------------------------------------------------------ + /// Standard "Dump" method. At present it does nothing. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) const; + +protected: + + // These are utility functions to assist with the search iteration. They are used by the + // default Search method. + + Searcher::CallbackReturn + DoModuleIteration (const SymbolContext &context, + Searcher &searcher); + + Searcher::CallbackReturn + DoModuleIteration (const lldb::ModuleSP& module_sp, + Searcher &searcher); + + Searcher::CallbackReturn + DoCUIteration (const lldb::ModuleSP& module_sp, + const SymbolContext &context, + Searcher &searcher); + + Searcher::CallbackReturn + DoFunctionIteration (Function *function, + const SymbolContext &context, + Searcher &searcher); + + lldb::TargetSP m_target_sp; // Every filter has to be associated with a target for + // now since you need a starting place for the search. +}; + +//---------------------------------------------------------------------- +/// @class SearchFilterForNonModuleSpecificSearches SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief This is a SearchFilter that searches through all modules. It also consults the Target::ModuleIsExcludedForNonModuleSpecificSearches. +//---------------------------------------------------------------------- +class SearchFilterForNonModuleSpecificSearches : + public SearchFilter +{ +public: + SearchFilterForNonModuleSpecificSearches (const lldb::TargetSP &targetSP) : SearchFilter(targetSP) {} + ~SearchFilterForNonModuleSpecificSearches () {} + + virtual bool + ModulePasses (const FileSpec &module_spec); + + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); +}; + +//---------------------------------------------------------------------- +/// @class SearchFilterByModule SearchFilter.h "lldb/Core/SearchFilter.h" +/// @brief This is a SearchFilter that restricts the search to a given module. +//---------------------------------------------------------------------- + +class SearchFilterByModule : + public SearchFilter +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search, + /// and the module to restrict the search to. + /// + /// @param[in] target + /// The Target that provides the module list to search. + /// + /// @param[in] module + /// The Module that limits the search. + //------------------------------------------------------------------ + SearchFilterByModule (const lldb::TargetSP &targetSP, + const FileSpec &module); + + SearchFilterByModule (const SearchFilterByModule& rhs); + + virtual + ~SearchFilterByModule (); + + const SearchFilterByModule& + operator=(const SearchFilterByModule& rhs); + + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); + + virtual bool + ModulePasses (const FileSpec &spec); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual uint32_t + GetFilterRequiredItems (); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpec m_module_spec; +}; + +class SearchFilterByModuleList : + public SearchFilter +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search, + /// and the module list to restrict the search to. + /// + /// @param[in] target + /// The Target that provides the module list to search. + /// + /// @param[in] module + /// The Module that limits the search. + //------------------------------------------------------------------ + SearchFilterByModuleList (const lldb::TargetSP &targetSP, + const FileSpecList &module_list); + + SearchFilterByModuleList (const SearchFilterByModuleList& rhs); + + virtual + ~SearchFilterByModuleList (); + + const SearchFilterByModuleList& + operator=(const SearchFilterByModuleList& rhs); + + virtual bool + ModulePasses (const lldb::ModuleSP &module_sp); + + virtual bool + ModulePasses (const FileSpec &spec); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual uint32_t + GetFilterRequiredItems (); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpecList m_module_spec_list; +}; + +class SearchFilterByModuleListAndCU : + public SearchFilterByModuleList +{ +public: + + //------------------------------------------------------------------ + /// The basic constructor takes a Target, which gives the space to search, + /// and the module list to restrict the search to. + /// + /// @param[in] target + /// The Target that provides the module list to search. + /// + /// @param[in] module + /// The Module that limits the search. + //------------------------------------------------------------------ + SearchFilterByModuleListAndCU (const lldb::TargetSP &targetSP, + const FileSpecList &module_list, + const FileSpecList &cu_list); + + SearchFilterByModuleListAndCU (const SearchFilterByModuleListAndCU& rhs); + + virtual + ~SearchFilterByModuleListAndCU (); + + const SearchFilterByModuleListAndCU& + operator=(const SearchFilterByModuleListAndCU& rhs); + + virtual bool + AddressPasses (Address &address); + + virtual bool + CompUnitPasses (FileSpec &fileSpec); + + virtual bool + CompUnitPasses (CompileUnit &compUnit); + + virtual void + GetDescription(Stream *s); + + virtual uint32_t + GetFilterRequiredItems (); + + virtual void + Dump (Stream *s) const; + + virtual void + Search (Searcher &searcher); + +private: + FileSpecList m_module_spec_list; + FileSpecList m_cu_spec_list; +}; + +} // namespace lldb_private + +#endif // liblldb_SearchFilter_h_ diff --git a/include/lldb/Core/Section.h b/include/lldb/Core/Section.h new file mode 100644 index 00000000000..437eaf59b9c --- /dev/null +++ b/include/lldb/Core/Section.h @@ -0,0 +1,313 @@ +//===-- Section.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Section_h_ +#define liblldb_Section_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/VMRange.h" +#include "lldb/Symbol/ObjectFile.h" +#include + +namespace lldb_private { + +class SectionList +{ +public: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + SectionList(); + + ~SectionList(); + + SectionList & + operator =(const SectionList& rhs); + + size_t + AddSection (const lldb::SectionSP& section_sp); + + size_t + AddUniqueSection (const lldb::SectionSP& section_sp); + + size_t + FindSectionIndex (const Section* sect); + + bool + ContainsSection(lldb::user_id_t sect_id) const; + + void + Dump (Stream *s, Target *target, bool show_header, uint32_t depth) const; + + lldb::SectionSP + FindSectionByName (const ConstString §ion_dstr) const; + + lldb::SectionSP + FindSectionByID (lldb::user_id_t sect_id) const; + + lldb::SectionSP + FindSectionByType (lldb::SectionType sect_type, bool check_children, size_t start_idx = 0) const; + + lldb::SectionSP + FindSectionContainingFileAddress (lldb::addr_t addr, uint32_t depth = UINT32_MAX) const; + + bool + GetSectionData (const DataExtractor& module_data, DataExtractor& section_data) const; + + // Get the number of sections in this list only + size_t + GetSize () const + { + return m_sections.size(); + } + + // Get the number of sections in this list, and any contained child sections + size_t + GetNumSections (uint32_t depth) const; + + bool + ReplaceSection (lldb::user_id_t sect_id, const lldb::SectionSP& section_sp, uint32_t depth = UINT32_MAX); + + // Warning, this can be slow as it's removing items from a std::vector. + bool + DeleteSection (size_t idx); + + lldb::SectionSP + GetSectionAtIndex (size_t idx) const; + + size_t + Slide (lldb::addr_t slide_amount, bool slide_children); + + void + Clear () + { + m_sections.clear(); + } + +protected: + collection m_sections; +}; + + +class Section : + public std::enable_shared_from_this
, + public ModuleChild, + public UserID, + public Flags +{ +public: + // Create a root section (one that has no parent) + Section (const lldb::ModuleSP &module_sp, + ObjectFile *obj_file, + lldb::user_id_t sect_id, + const ConstString &name, + lldb::SectionType sect_type, + lldb::addr_t file_vm_addr, + lldb::addr_t vm_size, + lldb::offset_t file_offset, + lldb::offset_t file_size, + uint32_t flags); + + // Create a section that is a child of parent_section_sp + Section (const lldb::SectionSP &parent_section_sp, // NULL for top level sections, non-NULL for child sections + const lldb::ModuleSP &module_sp, + ObjectFile *obj_file, + lldb::user_id_t sect_id, + const ConstString &name, + lldb::SectionType sect_type, + lldb::addr_t file_vm_addr, + lldb::addr_t vm_size, + lldb::offset_t file_offset, + lldb::offset_t file_size, + uint32_t flags); + + ~Section (); + + static int + Compare (const Section& a, const Section& b); + + bool + ContainsFileAddress (lldb::addr_t vm_addr) const; + + SectionList& + GetChildren () + { + return m_children; + } + + const SectionList& + GetChildren () const + { + return m_children; + } + + void + Dump (Stream *s, Target *target, uint32_t depth) const; + + void + DumpName (Stream *s) const; + + lldb::addr_t + GetLoadBaseAddress (Target *target) const; + + bool + ResolveContainedAddress (lldb::addr_t offset, Address &so_addr) const; + + lldb::offset_t + GetFileOffset () const + { + return m_file_offset; + } + + void + SetFileOffset (lldb::offset_t file_offset) + { + m_file_offset = file_offset; + } + + lldb::offset_t + GetFileSize () const + { + return m_file_size; + } + + void + SetFileSize (lldb::offset_t file_size) + { + m_file_size = file_size; + } + + lldb::addr_t + GetFileAddress () const; + + bool + SetFileAddress (lldb::addr_t file_addr); + + lldb::addr_t + GetOffset () const; + + + lldb::addr_t + GetByteSize () const + { + return m_byte_size; + } + + void + SetByteSize (lldb::addr_t byte_size) + { + m_byte_size = byte_size; + } + + bool + IsFake() const + { + return m_fake; + } + + void + SetIsFake(bool fake) + { + m_fake = fake; + } + + bool + IsEncrypted () const + { + return m_encrypted; + } + + void + SetIsEncrypted (bool b) + { + m_encrypted = b; + } + + bool + IsDescendant (const Section *section); + + const ConstString& + GetName () const + { + return m_name; + } + + bool + Slide (lldb::addr_t slide_amount, bool slide_children); + + + lldb::SectionType + GetType () const + { + return m_type; + } + + lldb::SectionSP + GetParent () const + { + return m_parent_wp.lock(); + } + + bool + IsThreadSpecific () const + { + return m_thread_specific; + } + + void + SetIsThreadSpecific (bool b) + { + m_thread_specific = b; + } + + ObjectFile * + GetObjectFile () + { + return m_obj_file; + } + const ObjectFile * + GetObjectFile () const + { + return m_obj_file; + } + + +protected: + + ObjectFile *m_obj_file; // The object file that data for this section should be read from + lldb::SectionType m_type; // The type of this section + lldb::SectionWP m_parent_wp; // Weak pointer to parent section + ConstString m_name; // Name of this section + lldb::addr_t m_file_addr; // The absolute file virtual address range of this section if m_parent == NULL, + // offset from parent file virtual address if m_parent != NULL + lldb::addr_t m_byte_size; // Size in bytes that this section will occupy in memory at runtime + lldb::offset_t m_file_offset; // Object file offset (if any) + lldb::offset_t m_file_size; // Object file size (can be smaller than m_byte_size for zero filled sections...) + SectionList m_children; // Child sections + bool m_fake:1, // If true, then this section only can contain the address if one of its + // children contains an address. This allows for gaps between the children + // that are contained in the address range for this section, but do not produce + // hits unless the children contain the address. + m_encrypted:1, // Set to true if the contents are encrypted + m_thread_specific:1;// This section is thread specific +private: + DISALLOW_COPY_AND_ASSIGN (Section); +}; + + +} // namespace lldb_private + +#endif // liblldb_Section_h_ diff --git a/include/lldb/Core/SourceManager.h b/include/lldb/Core/SourceManager.h new file mode 100644 index 00000000000..b850df774ba --- /dev/null +++ b/include/lldb/Core/SourceManager.h @@ -0,0 +1,196 @@ +//===-- SourceManager.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SourceManager_h_ +#define liblldb_SourceManager_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +class SourceManager +{ +public: +#ifndef SWIG + + class File + { + friend bool operator== (const SourceManager::File &lhs, const SourceManager::File &rhs); + public: + + File (const FileSpec &file_spec, Target *target); + ~File(); + + size_t + DisplaySourceLines (uint32_t line, + uint32_t context_before, + uint32_t context_after, + Stream *s); + void + FindLinesMatchingRegex (RegularExpression& regex, + uint32_t start_line, + uint32_t end_line, + std::vector &match_lines); + + bool + GetLine (uint32_t line_no, std::string &buffer); + + uint32_t + GetLineOffset (uint32_t line); + + bool + LineIsValid (uint32_t line); + + bool + FileSpecMatches (const FileSpec &file_spec); + + const FileSpec & + GetFileSpec () + { + return m_file_spec; + } + + uint32_t + GetSourceMapModificationID() const + { + return m_source_map_mod_id; + } + + protected: + + bool + CalculateLineOffsets (uint32_t line = UINT32_MAX); + + FileSpec m_file_spec_orig; // The original file spec that was used (can be different from m_file_spec) + FileSpec m_file_spec; // The actualy file spec being used (if the target has source mappings, this might be different from m_file_spec_orig) + TimeValue m_mod_time; // Keep the modification time that this file data is valid for + uint32_t m_source_map_mod_id; // If the target uses path remappings, be sure to clear our notion of a source file if the path modification ID changes + lldb::DataBufferSP m_data_sp; + typedef std::vector LineOffsets; + LineOffsets m_offsets; + }; + +#endif // SWIG + + typedef std::shared_ptr FileSP; + +#ifndef SWIG + + // The SourceFileCache class separates the source manager from the cache of source files, so the + // cache can be stored in the Debugger, but the source managers can be per target. + class SourceFileCache + { + public: + SourceFileCache () {} + ~SourceFileCache() {} + + void AddSourceFile (const FileSP &file_sp); + FileSP FindSourceFile (const FileSpec &file_spec) const; + + protected: + typedef std::map FileCache; + FileCache m_file_cache; + }; +#endif + + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + // A source manager can be made with a non-null target, in which case it can use the path remappings to find + // source files that are not in their build locations. With no target it won't be able to do this. + SourceManager (const lldb::DebuggerSP &debugger_sp); + SourceManager (const lldb::TargetSP &target_sp); + + ~SourceManager(); + + + FileSP + GetLastFile () + { + return m_last_file_sp; + } + + size_t + DisplaySourceLinesWithLineNumbers (const FileSpec &file, + uint32_t line, + uint32_t context_before, + uint32_t context_after, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs = NULL); + + // This variant uses the last file we visited. + size_t + DisplaySourceLinesWithLineNumbersUsingLastFile (uint32_t start_line, + uint32_t count, + uint32_t curr_line, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs = NULL); + + size_t + DisplayMoreWithLineNumbers (Stream *s, + uint32_t count, + bool reverse, + const SymbolContextList *bp_locs = NULL); + + bool + SetDefaultFileAndLine (const FileSpec &file_spec, uint32_t line); + + bool + GetDefaultFileAndLine (FileSpec &file_spec, uint32_t &line); + + bool + DefaultFileAndLineSet () + { + return (m_last_file_sp.get() != NULL); + } + + void + FindLinesMatchingRegex (FileSpec &file_spec, + RegularExpression& regex, + uint32_t start_line, + uint32_t end_line, + std::vector &match_lines); + +protected: + + FileSP + GetFile (const FileSpec &file_spec); + + //------------------------------------------------------------------ + // Classes that inherit from SourceManager can see and modify these + //------------------------------------------------------------------ + FileSP m_last_file_sp; + uint32_t m_last_line; + uint32_t m_last_count; + bool m_default_set; + lldb::TargetWP m_target_wp; + lldb::DebuggerWP m_debugger_wp; + +private: + //------------------------------------------------------------------ + // For SourceManager only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (SourceManager); +}; + +bool operator== (const SourceManager::File &lhs, const SourceManager::File &rhs); +} // namespace lldb_private + +#endif // liblldb_SourceManager_h_ diff --git a/include/lldb/Core/State.h b/include/lldb/Core/State.h new file mode 100644 index 00000000000..8057b3e0584 --- /dev/null +++ b/include/lldb/Core/State.h @@ -0,0 +1,78 @@ +//===-- State.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_State_h_ +#define liblldb_State_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//------------------------------------------------------------------ +/// Converts a StateType to a C string. +/// +/// @param[in] state +/// The StateType object to convert. +/// +/// @return +/// A NULL terminated C string that describes \a state. The +/// returned string comes from constant string buffers and does +/// not need to be freed. +//------------------------------------------------------------------ +const char * +StateAsCString (lldb::StateType state); + +//------------------------------------------------------------------ +/// Check if a state represents a state where the process or thread +/// is running. +/// +/// @param[in] state +/// The StateType enumeration value +/// +/// @return +/// \b true if the state represents a process or thread state +/// where the process or thread is running, \b false otherwise. +//------------------------------------------------------------------ +bool +StateIsRunningState (lldb::StateType state); + +//------------------------------------------------------------------ +/// Check if a state represents a state where the process or thread +/// is stopped. Stopped can mean stopped when the process is still +/// around, or stopped when the process has exited or doesn't exist +/// yet. The \a must_exist argument tells us which of these cases is +/// desired. +/// +/// @param[in] state +/// The StateType enumeration value +/// +/// @param[in] must_exist +/// A boolean that indicates the thread must also be alive +/// so states like unloaded or exited won't return true. +/// +/// @return +/// \b true if the state represents a process or thread state +/// where the process or thread is stopped. If \a must_exist is +/// \b true, then the process can't be exited or unloaded, +/// otherwise exited and unloaded or other states where the +/// process no longer exists are considered to be stopped. +//------------------------------------------------------------------ +bool +StateIsStoppedState (lldb::StateType state, bool must_exist); + +const char * +GetPermissionsAsCString (uint32_t permissions); + +} // namespace lldb_private + +#endif // liblldb_State_h_ diff --git a/include/lldb/Core/Stream.h b/include/lldb/Core/Stream.h new file mode 100644 index 00000000000..0fd4aac041a --- /dev/null +++ b/include/lldb/Core/Stream.h @@ -0,0 +1,612 @@ +//===-- Stream.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Stream_h_ +#define liblldb_Stream_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "lldb/Core/Flags.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Stream Stream.h "lldb/Core/Stream.h" +/// @brief A stream class that can stream formatted output to a file. +//---------------------------------------------------------------------- +class Stream +{ +public: + //------------------------------------------------------------------ + /// \a m_flags bit values. + //------------------------------------------------------------------ + enum + { + eVerbose = (1 << 0), ///< If set, verbose logging is enabled + eDebug = (1 << 1), ///< If set, debug logging is enabled + eAddPrefix = (1 << 2), ///< Add number prefixes for binary, octal and hex when eBinary is clear + eBinary = (1 << 3) ///< Get and put data as binary instead of as the default string mode. + }; + + //------------------------------------------------------------------ + /// Construct with flags and address size and byte order. + /// + /// Construct with dump flags \a flags and the default address + /// size. \a flags can be any of the above enumeration logical OR'ed + /// together. + //------------------------------------------------------------------ + Stream (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Construct a default Stream, not binary, host byte order and + /// host addr size. + /// + //------------------------------------------------------------------ + Stream (); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~Stream (); + + //------------------------------------------------------------------ + // Subclasses must override these methods + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Flush the stream. + /// + /// Subclasses should flush the stream to make any output appear + /// if the stream has any buffering. + //------------------------------------------------------------------ + virtual void + Flush () = 0; + + //------------------------------------------------------------------ + /// Output character bytes to the stream. + /// + /// Appends \a src_len characters from the buffer \a src to the + /// stream. + /// + /// @param[in] src + /// A buffer containing at least \a src_len bytes of data. + /// + /// @param[in] src_len + /// A number of bytes to append to the stream. + /// + /// @return + /// The number of bytes that were appended to the stream. + //------------------------------------------------------------------ + virtual size_t + Write (const void *src, size_t src_len) = 0; + + //------------------------------------------------------------------ + // Member functions + //------------------------------------------------------------------ + size_t + PutChar (char ch); + + //------------------------------------------------------------------ + /// Set the byte_order value. + /// + /// Sets the byte order of the data to extract. Extracted values + /// will be swapped if necessary when decoding. + /// + /// @param[in] byte_order + /// The byte order value to use when extracting data. + /// + /// @return + /// The old byte order value. + //------------------------------------------------------------------ + lldb::ByteOrder + SetByteOrder (lldb::ByteOrder byte_order); + + //------------------------------------------------------------------ + /// Format a C string from a printf style format and variable + /// arguments and encode and append the resulting C string as hex + /// bytes. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Any additional arguments needed for the printf format string. + /// + /// @return + /// The number of bytes that were appended to the stream. + //------------------------------------------------------------------ + size_t + PrintfAsRawHex8 (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + //------------------------------------------------------------------ + /// Format a C string from a printf style format and variable + /// arguments and encode and append the resulting C string as hex + /// bytes. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Any additional arguments needed for the printf format string. + /// + /// @return + /// The number of bytes that were appended to the stream. + //------------------------------------------------------------------ + size_t + PutHex8 (uint8_t uvalue); + + size_t + PutNHex8 (size_t n, uint8_t uvalue); + + size_t + PutHex16 (uint16_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutHex32 (uint32_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutHex64 (uint64_t uvalue, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutMaxHex64 (uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + size_t + PutFloat (float f, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutDouble (double d, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutLongDouble (long double ld, + lldb::ByteOrder byte_order = lldb::eByteOrderInvalid); + + size_t + PutPointer (void *ptr); + + // Append \a src_len bytes from \a src to the stream as hex characters + // (two ascii characters per byte of input data) + size_t + PutBytesAsRawHex8 (const void *src, + size_t src_len, + lldb::ByteOrder src_byte_order = lldb::eByteOrderInvalid, + lldb::ByteOrder dst_byte_order = lldb::eByteOrderInvalid); + + // Append \a src_len bytes from \a s to the stream as binary data. + size_t + PutRawBytes (const void *s, + size_t src_len, + lldb::ByteOrder src_byte_order = lldb::eByteOrderInvalid, + lldb::ByteOrder dst_byte_order = lldb::eByteOrderInvalid); + + size_t + PutCStringAsRawHex8 (const char *s); + + //------------------------------------------------------------------ + /// Output a NULL terminated C string \a cstr to the stream \a s. + /// + /// @param[in] cstr + /// A NULL terminated C string. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (const char *cstr); + + //------------------------------------------------------------------ + /// Output a pointer value \a p to the stream \a s. + /// + /// @param[in] p + /// A void pointer. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (void *p); + + //------------------------------------------------------------------ + /// Output a character \a ch to the stream \a s. + /// + /// @param[in] ch + /// A printable character value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (char ch); + + //------------------------------------------------------------------ + /// Output a uint8_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint8_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint8_t uval); + + //------------------------------------------------------------------ + /// Output a uint16_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint16_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint16_t uval); + + //------------------------------------------------------------------ + /// Output a uint32_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint32_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint32_t uval); + + //------------------------------------------------------------------ + /// Output a uint64_t \a uval to the stream \a s. + /// + /// @param[in] uval + /// A uint64_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (uint64_t uval); + + //------------------------------------------------------------------ + /// Output a int8_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int8_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int8_t sval); + + //------------------------------------------------------------------ + /// Output a int16_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int16_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int16_t sval); + + //------------------------------------------------------------------ + /// Output a int32_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int32_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int32_t sval); + + //------------------------------------------------------------------ + /// Output a int64_t \a sval to the stream \a s. + /// + /// @param[in] sval + /// A int64_t value. + /// + /// @return + /// A reference to this class so multiple things can be streamed + /// in one statement. + //------------------------------------------------------------------ + Stream& + operator<< (int64_t sval); + + //------------------------------------------------------------------ + /// Output an address value to this stream. + /// + /// Put an address \a addr out to the stream with optional \a prefix + /// and \a suffix strings. + /// + /// @param[in] addr + /// An address value. + /// + /// @param[in] addr_size + /// Size in bytes of the address, used for formatting. + /// + /// @param[in] prefix + /// A prefix C string. If NULL, no prefix will be output. + /// + /// @param[in] suffix + /// A suffix C string. If NULL, no suffix will be output. + //------------------------------------------------------------------ + void + Address (uint64_t addr, uint32_t addr_size, const char *prefix = NULL, const char *suffix = NULL); + + //------------------------------------------------------------------ + /// Output an address range to this stream. + /// + /// Put an address range \a lo_addr - \a hi_addr out to the stream + /// with optional \a prefix and \a suffix strings. + /// + /// @param[in] lo_addr + /// The start address of the address range. + /// + /// @param[in] hi_addr + /// The end address of the address range. + /// + /// @param[in] addr_size + /// Size in bytes of the address, used for formatting. + /// + /// @param[in] prefix + /// A prefix C string. If NULL, no prefix will be output. + /// + /// @param[in] suffix + /// A suffix C string. If NULL, no suffix will be output. + //------------------------------------------------------------------ + void + AddressRange(uint64_t lo_addr, uint64_t hi_addr, uint32_t addr_size, const char *prefix = NULL, const char *suffix = NULL); + + //------------------------------------------------------------------ + /// Output a C string to the stream. + /// + /// Print a C string \a cstr to the stream. + /// + /// @param[in] cstr + /// The string to be output to the stream. + //------------------------------------------------------------------ + size_t + PutCString (const char *cstr); + + //------------------------------------------------------------------ + /// Output and End of Line character to the stream. + //------------------------------------------------------------------ + size_t + EOL(); + + //------------------------------------------------------------------ + /// Get the address size in bytes. + /// + /// @return + /// The size of an address in bytes that is used when outputting + /// address and pointer values to the stream. + //------------------------------------------------------------------ + uint32_t + GetAddressByteSize () const; + + //------------------------------------------------------------------ + /// Test if debug logging is enabled. + /// + /// @return + // \b true if the debug flag bit is set in this stream, \b + // false otherwise. + //------------------------------------------------------------------ + bool + GetDebug() const; + + //------------------------------------------------------------------ + /// The flags accessor. + /// + /// @return + /// A reference to the Flags member variable. + //------------------------------------------------------------------ + Flags& + GetFlags(); + + //------------------------------------------------------------------ + /// The flags const accessor. + /// + /// @return + /// A const reference to the Flags member variable. + //------------------------------------------------------------------ + const Flags& + GetFlags() const; + + //------------------------------------------------------------------ + //// The byte order accessor. + //// + //// @return + //// The byte order. + //------------------------------------------------------------------ + lldb::ByteOrder + GetByteOrder() const; + + //------------------------------------------------------------------ + /// Get the current indentation level. + /// + /// @return + /// The current indentation level as an integer. + //------------------------------------------------------------------ + int + GetIndentLevel () const; + + //------------------------------------------------------------------ + /// Test if verbose logging is enabled. + /// + /// @return + // \b true if the verbose flag bit is set in this stream, \b + // false otherwise. + //------------------------------------------------------------------ + bool + GetVerbose() const; + + //------------------------------------------------------------------ + /// Indent the current line in the stream. + /// + /// Indent the current line using the current indentation level and + /// print an optional string following the idenatation spaces. + /// + /// @param[in] s + /// A C string to print following the indentation. If NULL, just + /// output the indentation characters. + //------------------------------------------------------------------ + size_t + Indent(const char *s = NULL); + + //------------------------------------------------------------------ + /// Decrement the current indentation level. + //------------------------------------------------------------------ + void + IndentLess (int amount = 2); + + //------------------------------------------------------------------ + /// Increment the current indentation level. + //------------------------------------------------------------------ + void + IndentMore (int amount = 2); + + //------------------------------------------------------------------ + /// Output an offset value. + /// + /// Put an offset \a uval out to the stream using the printf format + /// in \a format. + /// + /// @param[in] offset + /// The offset value. + /// + /// @param[in] format + /// The printf style format to use when outputting the offset. + //------------------------------------------------------------------ + void + Offset (uint32_t offset, const char *format = "0x%8.8x: "); + + //------------------------------------------------------------------ + /// Output printf formatted output to the stream. + /// + /// Print some formatted output to the stream. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + size_t + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + size_t + PrintfVarArg(const char *format, va_list args); + + //------------------------------------------------------------------ + /// Output a quoted C string value to the stream. + /// + /// Print a double quoted NULL terminated C string to the stream + /// using the printf format in \a format. + /// + /// @param[in] cstr + /// A NULL terminated C string value. + /// + /// @param[in] format + /// The optional C string format that can be overridden. + //------------------------------------------------------------------ + void + QuotedCString (const char *cstr, const char *format = "\"%s\""); + + //------------------------------------------------------------------ + /// Set the address size in bytes. + /// + /// @param[in] addr_size + /// The new size in bytes of an address to use when outputting + /// address and pointer values. + //------------------------------------------------------------------ + void + SetAddressByteSize (uint32_t addr_size); + + //------------------------------------------------------------------ + /// Set the current indentation level. + /// + /// @param[in] level + /// The new indentation level. + //------------------------------------------------------------------ + void + SetIndentLevel (int level); + + //------------------------------------------------------------------ + /// Output a SLEB128 number to the stream. + /// + /// Put an SLEB128 \a uval out to the stream using the printf format + /// in \a format. + /// + /// @param[in] uval + /// A uint64_t value that was extracted as a SLEB128 value. + /// + /// @param[in] format + /// The optional printf format that can be overridden. + //------------------------------------------------------------------ + size_t + PutSLEB128 (int64_t uval); + + //------------------------------------------------------------------ + /// Output a ULEB128 number to the stream. + /// + /// Put an ULEB128 \a uval out to the stream using the printf format + /// in \a format. + /// + /// @param[in] uval + /// A uint64_t value that was extracted as a ULEB128 value. + /// + /// @param[in] format + /// The optional printf format that can be overridden. + //------------------------------------------------------------------ + size_t + PutULEB128 (uint64_t uval); + + static void + UnitTest(Stream *s); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Flags m_flags; ///< Dump flags. + uint32_t m_addr_size; ///< Size of an address in bytes. + lldb::ByteOrder m_byte_order; ///< Byte order to use when encoding scalar types. + int m_indent_level; ///< Indention level. + + size_t _PutHex8 (uint8_t uvalue, bool add_prefix); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Stream_h_ + diff --git a/include/lldb/Core/StreamAsynchronousIO.h b/include/lldb/Core/StreamAsynchronousIO.h new file mode 100644 index 00000000000..0e3e9ee9bcf --- /dev/null +++ b/include/lldb/Core/StreamAsynchronousIO.h @@ -0,0 +1,42 @@ +//===-- StreamAsynchronousIO.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamAsynchronousIO_h_ +#define liblldb_StreamAsynchronousIO_h_ + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" + +namespace lldb_private { + +class StreamAsynchronousIO : + public Stream +{ +public: + StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type); + + virtual ~StreamAsynchronousIO (); + + virtual void + Flush (); + + virtual size_t + Write (const void *src, size_t src_len); + + +private: + Broadcaster &m_broadcaster; + uint32_t m_broadcast_event_type; + StreamString m_accumulated_data; +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamAsynchronousIO_h diff --git a/include/lldb/Core/StreamBuffer.h b/include/lldb/Core/StreamBuffer.h new file mode 100644 index 00000000000..9d25e842ecc --- /dev/null +++ b/include/lldb/Core/StreamBuffer.h @@ -0,0 +1,87 @@ +//===-- StreamBuffer.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamBuffer_h_ +#define liblldb_StreamBuffer_h_ + +#include +#include +#include "llvm/ADT/SmallVector.h" +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +template +class StreamBuffer : public Stream +{ +public: + StreamBuffer () : + Stream (0, 4, lldb::eByteOrderBig), + m_packet () + { + } + + + StreamBuffer (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order) : + Stream (flags, addr_size, byte_order), + m_packet () + { + } + + virtual + ~StreamBuffer () + { + } + + virtual void + Flush () + { + // Nothing to do when flushing a buffer based stream... + } + + virtual size_t + Write (const void *s, size_t length) + { + if (s && length) + m_packet.append ((const char *)s, ((const char *)s) + length); + return length; + } + + void + Clear() + { + m_packet.clear(); + } + + // Beware, this might not be NULL terminated as you can expect from + // StringString as there may be random bits in the llvm::SmallVector. If + // you are using this class to create a C string, be sure the call PutChar ('\0') + // after you have created your string, or use StreamString. + const char * + GetData () const + { + return m_packet.data(); + } + + size_t + GetSize() const + { + return m_packet.size(); + } + +protected: + llvm::SmallVector m_packet; + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_StreamBuffer_h_ diff --git a/include/lldb/Core/StreamCallback.h b/include/lldb/Core/StreamCallback.h new file mode 100644 index 00000000000..b5fb91c6ce0 --- /dev/null +++ b/include/lldb/Core/StreamCallback.h @@ -0,0 +1,47 @@ +//===-- StreamCallback.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamCallback_h_ +#define liblldb_StreamCallback_h_ + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class StreamCallback : + public Stream +{ +public: + StreamCallback (lldb::LogOutputCallback callback, void *baton); + + virtual ~StreamCallback (); + + virtual void + Flush (); + + virtual size_t + Write (const void *src, size_t src_len); + + +private: + typedef std::map collection; + lldb::LogOutputCallback m_callback; + void *m_baton; + collection m_accumulated_data; + Mutex m_collection_mutex; + + StreamString &FindStreamForThread(lldb::tid_t cur_tid); +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamCallback_h diff --git a/include/lldb/Core/StreamFile.h b/include/lldb/Core/StreamFile.h new file mode 100644 index 00000000000..d032c0b21e6 --- /dev/null +++ b/include/lldb/Core/StreamFile.h @@ -0,0 +1,75 @@ +//===-- StreamFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamFile_h_ +#define liblldb_StreamFile_h_ + +// C Includes +// C++ Includes + +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Stream.h" +#include "lldb/Host/File.h" + +namespace lldb_private { + +class StreamFile : public Stream +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StreamFile (); + + StreamFile (uint32_t flags, uint32_t addr_size, lldb::ByteOrder byte_order); + + StreamFile (int fd, bool transfer_ownership); + + StreamFile (const char *path); + + StreamFile (FILE *fh, bool transfer_ownership); + + virtual + ~StreamFile(); + + File & + GetFile () + { + return m_file; + } + + const File & + GetFile () const + { + return m_file; + } + + virtual void + Flush (); + + virtual size_t + Write (const void *s, size_t length); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from StreamFile can see and modify these + //------------------------------------------------------------------ + File m_file; + +private: + DISALLOW_COPY_AND_ASSIGN (StreamFile); +}; + +} // namespace lldb_private + +#endif // liblldb_StreamFile_h_ diff --git a/include/lldb/Core/StreamString.h b/include/lldb/Core/StreamString.h new file mode 100644 index 00000000000..a26ad2d16a0 --- /dev/null +++ b/include/lldb/Core/StreamString.h @@ -0,0 +1,64 @@ +//===-- StreamString.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamString_h_ +#define liblldb_StreamString_h_ + +#include + +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +class StreamString : public Stream +{ +public: + StreamString (); + + StreamString (uint32_t flags, + uint32_t addr_size, + lldb::ByteOrder byte_order); + + virtual + ~StreamString (); + + virtual void + Flush (); + + virtual size_t + Write (const void *s, size_t length); + + void + Clear(); + + bool + Empty() const; + + const char * + GetData () const; + + size_t + GetSize() const; + + std::string & + GetString(); + + const std::string & + GetString() const; + + void + FillLastLineToColumn (uint32_t column, char fill_char); + +protected: + std::string m_packet; + +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamString_h_ diff --git a/include/lldb/Core/StreamTee.h b/include/lldb/Core/StreamTee.h new file mode 100644 index 00000000000..e2a29a37455 --- /dev/null +++ b/include/lldb/Core/StreamTee.h @@ -0,0 +1,175 @@ +//===-- StreamTee.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StreamTee_h_ +#define liblldb_StreamTee_h_ + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class StreamTee : public Stream +{ +public: + StreamTee () : + Stream (), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams () + { + } + + StreamTee (lldb::StreamSP &stream_sp): + Stream (), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams () + { + // No need to lock mutex during construction + if (stream_sp) + m_streams.push_back (stream_sp); + } + + + StreamTee (lldb::StreamSP &stream_sp, lldb::StreamSP &stream_2_sp) : + Stream (), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams () + { + // No need to lock mutex during construction + if (stream_sp) + m_streams.push_back (stream_sp); + if (stream_2_sp) + m_streams.push_back (stream_2_sp); + } + + StreamTee (const StreamTee &rhs) : + Stream (rhs), + m_streams_mutex (Mutex::eMutexTypeRecursive), + m_streams() // Don't copy until we lock down "rhs" + { + Mutex::Locker locker (rhs.m_streams_mutex); + m_streams = rhs.m_streams; + } + + virtual + ~StreamTee () + { + } + + StreamTee & + operator = (const StreamTee &rhs) + { + if (this != &rhs) { + Stream::operator=(rhs); + Mutex::Locker lhs_locker (m_streams_mutex); + Mutex::Locker rhs_locker (rhs.m_streams_mutex); + m_streams = rhs.m_streams; + } + return *this; + } + + virtual void + Flush () + { + Mutex::Locker locker (m_streams_mutex); + collection::iterator pos, end; + for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) + { + // Allow for our collection to contain NULL streams. This allows + // the StreamTee to be used with hard coded indexes for clients + // that might want N total streams with only a few that are set + // to valid values. + Stream *strm = pos->get(); + if (strm) + strm->Flush (); + } + } + + virtual size_t + Write (const void *s, size_t length) + { + Mutex::Locker locker (m_streams_mutex); + if (m_streams.empty()) + return 0; + + size_t min_bytes_written = SIZE_MAX; + collection::iterator pos, end; + for (pos = m_streams.begin(), end = m_streams.end(); pos != end; ++pos) + { + // Allow for our collection to contain NULL streams. This allows + // the StreamTee to be used with hard coded indexes for clients + // that might want N total streams with only a few that are set + // to valid values. + Stream *strm = pos->get(); + if (strm) + { + const size_t bytes_written = strm->Write (s, length); + if (min_bytes_written > bytes_written) + min_bytes_written = bytes_written; + } + } + if (min_bytes_written == SIZE_MAX) + return 0; + return min_bytes_written; + } + + size_t + AppendStream (const lldb::StreamSP &stream_sp) + { + size_t new_idx = m_streams.size(); + Mutex::Locker locker (m_streams_mutex); + m_streams.push_back (stream_sp); + return new_idx; + } + + size_t + GetNumStreams () const + { + size_t result = 0; + { + Mutex::Locker locker (m_streams_mutex); + result = m_streams.size(); + } + return result; + } + + lldb::StreamSP + GetStreamAtIndex (uint32_t idx) + { + lldb::StreamSP stream_sp; + Mutex::Locker locker (m_streams_mutex); + if (idx < m_streams.size()) + stream_sp = m_streams[idx]; + return stream_sp; + } + + void + SetStreamAtIndex (uint32_t idx, const lldb::StreamSP& stream_sp) + { + Mutex::Locker locker (m_streams_mutex); + // Resize our stream vector as necessary to fit as many streams + // as needed. This also allows this class to be used with hard + // coded indexes that can be used contain many streams, not all + // of which are valid. + if (idx >= m_streams.size()) + m_streams.resize(idx + 1); + m_streams[idx] = stream_sp; + } + + +protected: + typedef std::vector collection; + mutable Mutex m_streams_mutex; + collection m_streams; +}; + +} // namespace lldb_private +#endif // #ifndef liblldb_StreamTee_h_ diff --git a/include/lldb/Core/StringList.h b/include/lldb/Core/StringList.h new file mode 100644 index 00000000000..5503274173c --- /dev/null +++ b/include/lldb/Core/StringList.h @@ -0,0 +1,107 @@ +//===-- StringList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StringList_h_ +#define liblldb_StringList_h_ + +#include + +#include "lldb/Core/STLUtils.h" +#include "lldb/lldb-forward.h" + +namespace lldb_private { + +class StringList +{ +public: + + StringList (); + + StringList (const char *str); + + StringList (const char **strv, int strc); + + virtual + ~StringList (); + + void + AppendString (const std::string &s); + + void + AppendString (const char *str); + + void + AppendString (const char *str, size_t str_len); + + void + AppendList (const char ** strv, int strc); + + void + AppendList (StringList strings); + + bool + ReadFileLines (FileSpec &input_file); + + size_t + GetSize () const; + + const char * + GetStringAtIndex (size_t idx) const; + + void + Join (const char *separator, Stream &strm); + + void + Clear (); + + void + LongestCommonPrefix (std::string &common_prefix); + + void + InsertStringAtIndex (size_t id, const char *str); + + void + DeleteStringAtIndex (size_t id); + + void + RemoveBlankLines (); + + size_t + SplitIntoLines (const char *lines, size_t len); + + std::string + CopyList(const char* item_preamble = NULL, + const char* items_sep = "\n"); + + StringList& + operator << (const char* str); + + StringList& + operator << (StringList strings); + + // This string list contains a list of valid auto completion + // strings, and the "s" is passed in. "matches" is filled in + // with zero or more string values that start with "s", and + // the first string to exactly match one of the string + // values in this collection, will have "exact_matches_idx" + // filled in to match the index, or "exact_matches_idx" will + // have SIZE_MAX + size_t + AutoComplete (const char *s, + StringList &matches, + size_t &exact_matches_idx) const; + +private: + + STLStringArray m_strings; +}; + +} // namespace lldb_private + +#endif // liblldb_StringList_h_ diff --git a/include/lldb/Core/ThreadSafeSTLMap.h b/include/lldb/Core/ThreadSafeSTLMap.h new file mode 100644 index 00000000000..703ce481f63 --- /dev/null +++ b/include/lldb/Core/ThreadSafeSTLMap.h @@ -0,0 +1,184 @@ +//===-- ThreadSafeSTLMap.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadSafeSTLMap_h_ +#define liblldb_ThreadSafeSTLMap_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +template +class ThreadSafeSTLMap +{ +public: + typedef std::map<_Key,_Tp> collection; + typedef typename collection::iterator iterator; + typedef typename collection::const_iterator const_iterator; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadSafeSTLMap() : + m_collection (), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ~ThreadSafeSTLMap() + { + } + + bool + IsEmpty() const + { + Mutex::Locker locker(m_mutex); + return m_collection.empty(); + } + + void + Clear() + { + Mutex::Locker locker(m_mutex); + return m_collection.clear(); + } + + size_t + Erase (const _Key& key) + { + Mutex::Locker locker(m_mutex); + return EraseNoLock (key); + } + + size_t + EraseNoLock (const _Key& key) + { + return m_collection.erase (key); + } + + bool + GetValueForKey (const _Key& key, _Tp &value) const + { + Mutex::Locker locker(m_mutex); + return GetValueForKeyNoLock (key, value); + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + bool + GetValueForKeyNoLock (const _Key& key, _Tp &value) const + { + const_iterator pos = m_collection.find(key); + if (pos != m_collection.end()) + { + value = pos->second; + return true; + } + return false; + } + + bool + GetFirstKeyForValue (const _Tp &value, _Key& key) const + { + Mutex::Locker locker(m_mutex); + return GetFirstKeyForValueNoLock (value, key); + } + + bool + GetFirstKeyForValueNoLock (const _Tp &value, _Key& key) const + { + const_iterator pos, end = m_collection.end(); + for (pos = m_collection.begin(); pos != end; ++pos) + { + if (pos->second == value) + { + key = pos->first; + return true; + } + } + return false; + } + + bool + LowerBound (const _Key& key, + _Key& match_key, + _Tp &match_value, + bool decrement_if_not_equal) const + { + Mutex::Locker locker(m_mutex); + return LowerBoundNoLock (key, match_key, match_value, decrement_if_not_equal); + } + + bool + LowerBoundNoLock (const _Key& key, + _Key& match_key, + _Tp &match_value, + bool decrement_if_not_equal) const + { + const_iterator pos = m_collection.lower_bound (key); + if (pos != m_collection.end()) + { + match_key = pos->first; + if (decrement_if_not_equal && key != match_key && pos != m_collection.begin()) + { + --pos; + match_key = pos->first; + } + match_value = pos->second; + return true; + } + return false; + } + + iterator + lower_bound_unsafe (const _Key& key) + { + return m_collection.lower_bound (key); + } + + void + SetValueForKey (const _Key& key, const _Tp &value) + { + Mutex::Locker locker(m_mutex); + SetValueForKeyNoLock (key, value); + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + void + SetValueForKeyNoLock (const _Key& key, const _Tp &value) + { + m_collection[key] = value; + } + + Mutex & + GetMutex () + { + return m_mutex; + } + +private: + collection m_collection; + mutable Mutex m_mutex; + + //------------------------------------------------------------------ + // For ThreadSafeSTLMap only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ThreadSafeSTLMap); +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadSafeSTLMap_h_ diff --git a/include/lldb/Core/ThreadSafeValue.h b/include/lldb/Core/ThreadSafeValue.h new file mode 100644 index 00000000000..42a5a5c6725 --- /dev/null +++ b/include/lldb/Core/ThreadSafeValue.h @@ -0,0 +1,96 @@ +//===-- ThreadSafeValue.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadSafeValue_h_ +#define liblldb_ThreadSafeValue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +template +class ThreadSafeValue +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadSafeValue() : + m_value (), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ThreadSafeValue(const T& value) : + m_value (value), + m_mutex (Mutex::eMutexTypeRecursive) + { + } + + ~ThreadSafeValue() + { + } + + T + GetValue () const + { + T value; + { + Mutex::Locker locker(m_mutex); + value = m_value; + } + return value; + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + const T& + GetValueNoLock () const + { + return m_value; + } + + void + SetValue (const T& value) + { + Mutex::Locker locker(m_mutex); + m_value = value; + } + + // Call this if you have already manually locked the mutex using the + // GetMutex() accessor + void + SetValueNoLock (const T& value) + { + m_value = value; + } + + Mutex & + GetMutex () + { + return m_mutex; + } + +private: + T m_value; + mutable Mutex m_mutex; + + //------------------------------------------------------------------ + // For ThreadSafeValue only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ThreadSafeValue); +}; + + +} // namespace lldb_private +#endif // liblldb_ThreadSafeValue_h_ diff --git a/include/lldb/Core/Timer.h b/include/lldb/Core/Timer.h new file mode 100644 index 00000000000..e354d91be44 --- /dev/null +++ b/include/lldb/Core/Timer.h @@ -0,0 +1,160 @@ +//===-- Timer.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Timer_h_ +#define liblldb_Timer_h_ +#if defined(__cplusplus) + +#include +#include +#include +#include "lldb/lldb-private.h" +#include "lldb/Host/TimeValue.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Timer Timer.h "lldb/Core/Timer.h" +/// @brief A timer class that simplifies common timing metrics. +/// +/// A scoped timer class that allows a variety of pthread mutex +/// objects to have a mutex locked when a Timer::Locker +/// object is created, and unlocked when it goes out of scope or +/// when the Timer::Locker::Reset(pthread_mutex_t *) +/// is called. This provides an exception safe way to lock a mutex +/// in a scope. +//---------------------------------------------------------------------- + +class Timer +{ +public: + static void + Initialize (); + + //-------------------------------------------------------------- + /// Default constructor. + //-------------------------------------------------------------- + Timer(const char *category, const char *format, ...) __attribute__ ((format (printf, 3, 4))); + + //-------------------------------------------------------------- + /// Desstructor + //-------------------------------------------------------------- + ~Timer(); + + void + Dump (); + + static void + SetDisplayDepth (uint32_t depth); + + static void + SetQuiet (bool value); + + static void + DumpCategoryTimes (Stream *s); + + static void + ResetCategoryTimes (); + +protected: + + void + ChildStarted (const TimeValue& time); + + void + ChildStopped (const TimeValue& time); + + uint64_t + GetTotalElapsedNanoSeconds(); + + uint64_t + GetTimerElapsedNanoSeconds(); + + //-------------------------------------------------------------- + /// Member variables + //-------------------------------------------------------------- + const char *m_category; + TimeValue m_total_start; + TimeValue m_timer_start; + uint64_t m_total_ticks; // Total running time for this timer including when other timers below this are running + uint64_t m_timer_ticks; // Ticks for this timer that do not include when other timers below this one are running + static uint32_t g_depth; + static uint32_t g_display_depth; + static FILE * g_file; +private: + Timer(); + DISALLOW_COPY_AND_ASSIGN (Timer); +}; + +class IntervalTimer +{ +public: + IntervalTimer() : + m_start (TimeValue::Now()) + { + } + + ~IntervalTimer() + { + } + + uint64_t + GetElapsedNanoSeconds() const + { + return TimeValue::Now() - m_start; + } + + void + Reset () + { + m_start = TimeValue::Now(); + } + + int + PrintfElapsed (const char *format, ...) __attribute__ ((format (printf, 2, 3))) + { + TimeValue now (TimeValue::Now()); + const uint64_t elapsed_nsec = now - m_start; + const char *unit = NULL; + float elapsed_value; + if (elapsed_nsec < 1000) + { + unit = "ns"; + elapsed_value = (float)elapsed_nsec; + } + else if (elapsed_nsec < 1000000) + { + unit = "us"; + elapsed_value = (float)elapsed_nsec/1000.0f; + } + else if (elapsed_nsec < 1000000000) + { + unit = "ms"; + elapsed_value = (float)elapsed_nsec/1000000.0f; + } + else + { + unit = "sec"; + elapsed_value = (float)elapsed_nsec/1000000000.0f; + } + int result = printf ("%3.2f %s: ", elapsed_value, unit); + va_list args; + va_start (args, format); + result += vprintf (format, args); + va_end (args); + return result; + } +protected: + TimeValue m_start; +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Timer_h_ diff --git a/include/lldb/Core/UUID.h b/include/lldb/Core/UUID.h new file mode 100644 index 00000000000..fe72b8eb0c7 --- /dev/null +++ b/include/lldb/Core/UUID.h @@ -0,0 +1,109 @@ +//===-- UUID.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UUID_h_ +#define liblldb_UUID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class UUID +{ +public: + // Most UUIDs are 16 bytes, but some Linux build-ids (SHA1) are 20. + typedef uint8_t ValueType[20]; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + UUID (); + UUID (const UUID& rhs); + UUID (const void *uuid_bytes, uint32_t num_uuid_bytes); + + ~UUID (); + + const UUID& + operator=(const UUID& rhs); + + void + Clear (); + + void + Dump (Stream *s) const; + + const void * + GetBytes() const; + + size_t + GetByteSize(); + + bool + IsValid () const; + + bool + SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes = 16); + + std::string + GetAsString (const char *separator = NULL) const; + + size_t + SetFromCString (const char *c_str, uint32_t num_uuid_bytes = 16); + + // Decode as many UUID bytes (up to 16) as possible from the C string "cstr" + // This is used for auto completion where a partial UUID might have been + // typed in. It + //------------------------------------------------------------------ + /// Decode as many UUID bytes (up to 16) as possible from the C + /// string \a cstr. + /// + /// @param[in] cstr + /// A NULL terminate C string that points at a UUID string value + /// (no leading spaces). The string must contain only hex + /// characters and optionally can contain the '-' sepearators. + /// + /// @param[in] uuid_bytes + /// A buffer of bytes that will contain a full or patially + /// decoded UUID. + /// + /// @param[out] end + /// If \a end is not NULL, it will be filled in with the a + /// pointer to the character after the last successfully decoded + /// byte. + /// + /// @return + /// Returns the number of bytes that were successfully decoded + /// which should be 16 if a full UUID value was properly decoded. + //------------------------------------------------------------------ + static size_t + DecodeUUIDBytesFromCString (const char *cstr, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes = 16); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from UUID can see and modify these + //------------------------------------------------------------------ + uint32_t m_num_uuid_bytes; // Should be 16 or 20 + ValueType m_uuid; +}; + +bool operator == (const UUID &lhs, const UUID &rhs); +bool operator != (const UUID &lhs, const UUID &rhs); +bool operator < (const UUID &lhs, const UUID &rhs); +bool operator <= (const UUID &lhs, const UUID &rhs); +bool operator > (const UUID &lhs, const UUID &rhs); +bool operator >= (const UUID &lhs, const UUID &rhs); + +} // namespace lldb_private + +#endif // liblldb_UUID_h_ diff --git a/include/lldb/Core/UniqueCStringMap.h b/include/lldb/Core/UniqueCStringMap.h new file mode 100644 index 00000000000..972c0d53ea9 --- /dev/null +++ b/include/lldb/Core/UniqueCStringMap.h @@ -0,0 +1,361 @@ +//===-- UniqueCStringMap.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UniqueCStringMap_h_ +#define liblldb_UniqueCStringMap_h_ +#if defined(__cplusplus) + +#include +#include +#include + +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private { + + + +//---------------------------------------------------------------------- +// Templatized uniqued string map. +// +// This map is useful for mapping unique C string names to values of +// type T. Each "const char *" name added must be unique for a given +// C string value. ConstString::GetCString() can provide such strings. +// Any other string table that has guaranteed unique values can also +// be used. +//---------------------------------------------------------------------- +template +class UniqueCStringMap +{ +public: + struct Entry + { + Entry () : + cstring(NULL), + value() + { + } + + Entry (const char *cstr) : + cstring(cstr), + value() + { + } + + Entry (const char *cstr, const T&v) : + cstring(cstr), + value(v) + { + } + + bool + operator < (const Entry& rhs) const + { + return cstring < rhs.cstring; + } + + const char* cstring; + T value; + }; + + //------------------------------------------------------------------ + // Call this function multiple times to add a bunch of entries to + // this map, then later call UniqueCStringMap::Sort() before doing + // any searches by name. + //------------------------------------------------------------------ + void + Append (const char *unique_cstr, const T& value) + { + m_map.push_back (typename UniqueCStringMap::Entry(unique_cstr, value)); + } + + void + Append (const Entry &e) + { + m_map.push_back (e); + } + + void + Clear () + { + m_map.clear(); + } + + //------------------------------------------------------------------ + // Call this function to always keep the map sorted when putting + // entries into the map. + //------------------------------------------------------------------ + void + Insert (const char *unique_cstr, const T& value) + { + typename UniqueCStringMap::Entry e(unique_cstr, value); + m_map.insert (std::upper_bound (m_map.begin(), m_map.end(), e), e); + } + + void + Insert (const Entry &e) + { + m_map.insert (std::upper_bound (m_map.begin(), m_map.end(), e), e); + } + + //------------------------------------------------------------------ + // Get an entries by index in a variety of forms. + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned values. + //------------------------------------------------------------------ + bool + GetValueAtIndex (uint32_t idx, T &value) const + { + if (idx < m_map.size()) + { + value = m_map[idx].value; + return true; + } + return false; + } + + const char * + GetCStringAtIndexUnchecked (uint32_t idx) const + { + return m_map[idx].cstring; + } + + // Use this function if you have simple types in your map that you + // can easily copy when accessing values by index. + T + GetValueAtIndexUnchecked (uint32_t idx) const + { + return m_map[idx].value; + } + + // Use this function if you have complex types in your map that you + // don't want to copy when accessing values by index. + const T & + GetValueRefAtIndexUnchecked (uint32_t idx) const + { + return m_map[idx].value; + } + + const char * + GetCStringAtIndex (uint32_t idx) const + { + if (idx < m_map.size()) + return m_map[idx].cstring; + return NULL; + } + + //------------------------------------------------------------------ + // Find the value for the unique string in the map. + // + // Return the value for \a unique_cstr if one is found, return + // \a fail_value otherwise. This method works well for simple type + // T values and only if there is a sensible failure value that can + // be returned and that won't match any existing values. + //------------------------------------------------------------------ + T + Find (const char *unique_cstr, T fail_value) const + { + Entry search_entry (unique_cstr); + const_iterator end = m_map.end(); + const_iterator pos = std::lower_bound (m_map.begin(), end, search_entry); + if (pos != end) + { + if (pos->cstring == unique_cstr) + return pos->value; + } + return fail_value; + } + //------------------------------------------------------------------ + // Get a pointer to the first entry that matches "name". NULL will + // be returned if there is no entry that matches "name". + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned pointer. + //------------------------------------------------------------------ + const Entry * + FindFirstValueForName (const char *unique_cstr) const + { + Entry search_entry (unique_cstr); + const_iterator end = m_map.end(); + const_iterator pos = std::lower_bound (m_map.begin(), end, search_entry); + if (pos != end) + { + const char *pos_cstr = pos->cstring; + if (pos_cstr == unique_cstr) + return &(*pos); + } + return NULL; + } + + //------------------------------------------------------------------ + // Get a pointer to the next entry that matches "name" from a + // previously returned Entry pointer. NULL will be returned if there + // is no subsequent entry that matches "name". + // + // The caller is responsible for ensuring that the collection does + // not change during while using the returned pointer. + //------------------------------------------------------------------ + const Entry * + FindNextValueForName (const Entry *entry_ptr) const + { + if (!m_map.empty()) + { + const Entry *first_entry = &m_map[0]; + const Entry *after_last_entry = first_entry + m_map.size(); + const Entry *next_entry = entry_ptr + 1; + if (first_entry <= next_entry && next_entry < after_last_entry) + { + if (next_entry->cstring == entry_ptr->cstring) + return next_entry; + } + } + return NULL; + } + + size_t + GetValues (const char *unique_cstr, std::vector &values) const + { + const size_t start_size = values.size(); + + Entry search_entry (unique_cstr); + const_iterator pos, end = m_map.end(); + for (pos = std::lower_bound (m_map.begin(), end, search_entry); pos != end; ++pos) + { + if (pos->cstring == unique_cstr) + values.push_back (pos->value); + else + break; + } + + return values.size() - start_size; + } + + size_t + GetValues (const RegularExpression& regex, std::vector &values) const + { + const size_t start_size = values.size(); + + const_iterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; ++pos) + { + if (regex.Execute(pos->cstring)) + values.push_back (pos->value); + } + + return values.size() - start_size; + } + + //------------------------------------------------------------------ + // Get the total number of entries in this map. + //------------------------------------------------------------------ + size_t + GetSize () const + { + return m_map.size(); + } + + + //------------------------------------------------------------------ + // Returns true if this map is empty. + //------------------------------------------------------------------ + bool + IsEmpty() const + { + return m_map.empty(); + } + + //------------------------------------------------------------------ + // Reserve memory for at least "n" entries in the map. This is + // useful to call when you know you will be adding a lot of entries + // using UniqueCStringMap::Append() (which should be followed by a + // call to UniqueCStringMap::Sort()) or to UniqueCStringMap::Insert(). + //------------------------------------------------------------------ + void + Reserve (size_t n) + { + m_map.reserve (n); + } + + //------------------------------------------------------------------ + // Sort the unsorted contents in this map. A typical code flow would + // be: + // size_t approximate_num_entries = .... + // UniqueCStringMap my_map; + // my_map.Reserve (approximate_num_entries); + // for (...) + // { + // my_map.Append (UniqueCStringMap::Entry(GetName(...), GetValue(...))); + // } + // my_map.Sort(); + //------------------------------------------------------------------ + void + Sort () + { + std::sort (m_map.begin(), m_map.end()); + } + + //------------------------------------------------------------------ + // Since we are using a vector to contain our items it will always + // double its memory consumption as things are added to the vector, + // so if you intend to keep a UniqueCStringMap around and have + // a lot of entries in the map, you will want to call this function + // to create a new vector and copy _only_ the exact size needed as + // part of the finalization of the string map. + //------------------------------------------------------------------ + void + SizeToFit () + { + if (m_map.size() < m_map.capacity()) + { + collection temp (m_map.begin(), m_map.end()); + m_map.swap(temp); + } + } + + size_t + Erase (const char *unique_cstr) + { + size_t num_removed = 0; + Entry search_entry (unique_cstr); + iterator end = m_map.end(); + iterator begin = m_map.begin(); + iterator lower_pos = std::lower_bound (begin, end, search_entry); + if (lower_pos != end) + { + if (lower_pos->cstring == unique_cstr) + { + iterator upper_pos = std::upper_bound (lower_pos, end, search_entry); + if (lower_pos == upper_pos) + { + m_map.erase (lower_pos); + num_removed = 1; + } + else + { + num_removed = std::distance (lower_pos, upper_pos); + m_map.erase (lower_pos, upper_pos); + } + } + } + return num_removed; + } +protected: + typedef std::vector collection; + typedef typename collection::iterator iterator; + typedef typename collection::const_iterator const_iterator; + collection m_map; +}; + + + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_UniqueCStringMap_h_ diff --git a/include/lldb/Core/UserID.h b/include/lldb/Core/UserID.h new file mode 100644 index 00000000000..ea6af74759b --- /dev/null +++ b/include/lldb/Core/UserID.h @@ -0,0 +1,130 @@ +//===-- UserID.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_UserID_h_ +#define liblldb_UserID_h_ + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class UserID UserID.h "lldb/Core/UserID.h" +/// @brief A mix in class that contains a generic user ID. +/// +/// UserID is desinged as a mix in class that can contain an integer +/// based unique identifier for a varietly of objects in lldb. +/// +/// The value for this identifier is chosen by each parser plug-in. A +/// value should be chosen that makes sense for each kind of object +/// should and allows quick access to further and more in depth parsing. +/// +/// Symbol table entries can use this to store the original symbol table +/// index, functions can use it to store the symbol table index or the +/// DWARF offset. +//---------------------------------------------------------------------- +struct UserID +{ + //------------------------------------------------------------------ + /// Construct with optional user ID. + //------------------------------------------------------------------ + UserID (lldb::user_id_t uid = LLDB_INVALID_UID) : m_uid(uid) {} + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~UserID () + { + } + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clears the object contents back to a default invalid state. + //------------------------------------------------------------------ + void + Clear () { m_uid = LLDB_INVALID_UID; } + + //------------------------------------------------------------------ + /// Get accessor for the user ID. + /// + /// @return + /// The user ID. + //------------------------------------------------------------------ + lldb::user_id_t + GetID () const { return m_uid; } + + //------------------------------------------------------------------ + /// Set accessor for the user ID. + /// + /// @param[in] uid + /// The new user ID. + //------------------------------------------------------------------ + void + SetID (lldb::user_id_t uid) { m_uid = uid; } + + //------------------------------------------------------------------ + /// Unary predicate function object that can search for a matching + /// user ID. + /// + /// Function object that can be used on any class that inherits + /// from UserID: + /// \code + /// iterator pos; + /// pos = std::find_if (coll.begin(), coll.end(), UserID::IDMatches(blockID)); + /// \endcode + //------------------------------------------------------------------ + class IDMatches + { + public: + //-------------------------------------------------------------- + /// Construct with the user ID to look for. + //-------------------------------------------------------------- + IDMatches (lldb::user_id_t uid) : m_uid(uid) {} + + //-------------------------------------------------------------- + /// Unary predicate function object callback. + //-------------------------------------------------------------- + bool + operator () (const UserID& rhs) const { return m_uid == rhs.GetID(); } + + private: + //-------------------------------------------------------------- + // Member variables. + //-------------------------------------------------------------- + const lldb::user_id_t m_uid; ///< The user ID we are looking for + }; + + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + lldb::user_id_t m_uid; ///< The user ID that uniquely identifies an object. +}; + +inline bool operator== (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() == rhs.GetID(); +} + +inline bool operator!= (const UserID& lhs, const UserID& rhs) +{ + return lhs.GetID() != rhs.GetID(); +} + +//-------------------------------------------------------------- +/// Stream the UserID object to a Stream. +//-------------------------------------------------------------- +Stream& operator << (Stream& strm, const UserID& uid); + +} // namespace lldb_private + +#endif // liblldb_UserID_h_ diff --git a/include/lldb/Core/UserSettingsController.h b/include/lldb/Core/UserSettingsController.h new file mode 100644 index 00000000000..7e72b89ad8e --- /dev/null +++ b/include/lldb/Core/UserSettingsController.h @@ -0,0 +1,98 @@ +//====-- UserSettingsController.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_UserSettingsController_h_ +#define liblldb_UserSettingsController_h_ + +// C Includes +// C++ Includes + +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class Properties +{ +public: + Properties () : + m_collection_sp () + { + } + + Properties (const lldb::OptionValuePropertiesSP &collection_sp) : + m_collection_sp (collection_sp) + { + } + + virtual + ~Properties() + { + } + + virtual lldb::OptionValuePropertiesSP + GetValueProperties () const + { + // This function is virtual in case subclasses want to lazily + // implement creating the properties. + return m_collection_sp; + } + + virtual lldb::OptionValueSP + GetPropertyValue (const ExecutionContext *exe_ctx, + const char *property_path, + bool will_modify, + Error &error) const; + + virtual Error + SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value); + + virtual Error + DumpPropertyValue (const ExecutionContext *exe_ctx, + Stream &strm, + const char *property_path, + uint32_t dump_mask); + + virtual void + DumpAllPropertyValues (const ExecutionContext *exe_ctx, + Stream &strm, + uint32_t dump_mask); + + virtual void + DumpAllDescriptions (CommandInterpreter &interpreter, + Stream &strm) const; + + size_t + Apropos (const char *keyword, + std::vector &matching_properties) const; + + lldb::OptionValuePropertiesSP + GetSubProperty (const ExecutionContext *exe_ctx, + const ConstString &name); +protected: + lldb::OptionValuePropertiesSP m_collection_sp; +}; + +} // namespace lldb_private + +#endif // liblldb_UserSettingsController_h_ diff --git a/include/lldb/Core/VMRange.h b/include/lldb/Core/VMRange.h new file mode 100644 index 00000000000..94c83e730e9 --- /dev/null +++ b/include/lldb/Core/VMRange.h @@ -0,0 +1,181 @@ +//===-- VMRange.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_VMRange_h_ +#define liblldb_VMRange_h_ + +#include "lldb/lldb-private.h" +#include + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A vm address range. These can represent offsets ranges or actual +// addresses. +//---------------------------------------------------------------------- +class VMRange +{ +public: + + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + VMRange() : + m_base_addr(0), + m_byte_size(0) + { + } + + VMRange(lldb::addr_t start_addr, lldb::addr_t end_addr) : + m_base_addr(start_addr), + m_byte_size(end_addr > start_addr ? end_addr - start_addr : 0) + { + } + + ~VMRange() + { + } + + void + Clear () + { + m_base_addr = 0; + m_byte_size = 0; + } + + // Set the start and end values + void + Reset (lldb::addr_t start_addr, lldb::addr_t end_addr) + { + SetBaseAddress (start_addr); + SetEndAddress (end_addr); + } + + // Set the start value for the range, and keep the same size + void + SetBaseAddress (lldb::addr_t base_addr) + { + m_base_addr = base_addr; + } + + void + SetEndAddress (lldb::addr_t end_addr) + { + const lldb::addr_t base_addr = GetBaseAddress(); + if (end_addr > base_addr) + m_byte_size = end_addr - base_addr; + else + m_byte_size = 0; + } + + lldb::addr_t + GetByteSize () const + { + return m_byte_size; + } + + void + SetByteSize (lldb::addr_t byte_size) + { + m_byte_size = byte_size; + } + + lldb::addr_t + GetBaseAddress () const + { + return m_base_addr; + } + + lldb::addr_t + GetEndAddress () const + { + return GetBaseAddress() + m_byte_size; + } + + bool + IsValid() const + { + return m_byte_size > 0; + } + + bool + Contains (lldb::addr_t addr) const + { + return (GetBaseAddress() <= addr) && (addr < GetEndAddress()); + } + + bool + Contains (const VMRange& range) const + { + if (Contains(range.GetBaseAddress())) + { + lldb::addr_t range_end = range.GetEndAddress(); + return (GetBaseAddress() <= range_end) && (range_end <= GetEndAddress()); + } + return false; + } + + void + Dump (Stream *s, lldb::addr_t base_addr = 0, uint32_t addr_width = 8) const; + + class ValueInRangeUnaryPredicate + { + public: + ValueInRangeUnaryPredicate(lldb::addr_t value) : + _value(value) + { + } + bool operator()(const VMRange& range) const + { + return range.Contains(_value); + } + lldb::addr_t _value; + }; + + class RangeInRangeUnaryPredicate + { + public: + RangeInRangeUnaryPredicate(VMRange range) : + _range(range) + { + } + bool operator()(const VMRange& range) const + { + return range.Contains(_range); + } + const VMRange& _range; + }; + + static bool + ContainsValue(const VMRange::collection& coll, lldb::addr_t value); + + static bool + ContainsRange(const VMRange::collection& coll, const VMRange& range); + + // Returns a valid index into coll when a match is found, else UINT32_MAX + // is returned + static size_t + FindRangeIndexThatContainsValue (const VMRange::collection& coll, lldb::addr_t value); + +protected: + lldb::addr_t m_base_addr; + lldb::addr_t m_byte_size; +}; + +bool operator== (const VMRange& lhs, const VMRange& rhs); +bool operator!= (const VMRange& lhs, const VMRange& rhs); +bool operator< (const VMRange& lhs, const VMRange& rhs); +bool operator<= (const VMRange& lhs, const VMRange& rhs); +bool operator> (const VMRange& lhs, const VMRange& rhs); +bool operator>= (const VMRange& lhs, const VMRange& rhs); + +} // namespace lldb_private + +#endif // liblldb_VMRange_h_ diff --git a/include/lldb/Core/Value.h b/include/lldb/Core/Value.h new file mode 100644 index 00000000000..5461ca73d08 --- /dev/null +++ b/include/lldb/Core/Value.h @@ -0,0 +1,314 @@ +//===-- Value.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Value_h_ +#define liblldb_Value_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private { + +class Value +{ +public: + + // Values Less than zero are an error, greater than or equal to zero + // returns what the Scalar result is. + enum ValueType + { + // m_value contains... + // ============================ + eValueTypeScalar, // raw scalar value + eValueTypeVector, // byte array of m_vector.length with endianness of m_vector.byte_order + eValueTypeFileAddress, // file address value + eValueTypeLoadAddress, // load address value + eValueTypeHostAddress // host address value (for memory in the process that is using liblldb) + }; + + enum ContextType // Type that describes Value::m_context + { + // m_context contains... + // ==================== + eContextTypeInvalid, // undefined + eContextTypeRegisterInfo, // RegisterInfo * (can be a scalar or a vector register) + eContextTypeLLDBType, // lldb_private::Type * + eContextTypeVariable // lldb_private::Variable * + }; + + const static size_t kMaxByteSize = 32u; + + struct Vector + { + // The byte array must be big enough to hold vector registers for any supported target. + uint8_t bytes[kMaxByteSize]; + size_t length; + lldb::ByteOrder byte_order; + + Vector() : + length(0), + byte_order(lldb::eByteOrderInvalid) + { + } + + Vector(const Vector& vector) + { *this = vector; + } + const Vector& + operator=(const Vector& vector) + { + SetBytes(vector.bytes, vector.length, vector.byte_order); + return *this; + } + + void + Clear () + { + length = 0; + } + + bool + SetBytes(const void *bytes, size_t length, lldb::ByteOrder byte_order) + { + this->length = length; + this->byte_order = byte_order; + if (length) + ::memcpy(this->bytes, bytes, length < kMaxByteSize ? length : kMaxByteSize); + return IsValid(); + } + + bool + IsValid() const + { + return (length > 0 && length < kMaxByteSize && byte_order != lldb::eByteOrderInvalid); + } + // Casts a vector, if valid, to an unsigned int of matching or largest supported size. + // Truncates to the beginning of the vector if required. + // Returns a default constructed Scalar if the Vector data is internally inconsistent. + Scalar + GetAsScalar() const + { + Scalar scalar; + if (IsValid()) + { + if (length == 1) scalar = *(const uint8_t *)bytes; + else if (length == 2) scalar = *(const uint16_t *)bytes; + else if (length == 4) scalar = *(const uint32_t *)bytes; + else if (length == 8) scalar = *(const uint64_t *)bytes; +#if defined (ENABLE_128_BIT_SUPPORT) + else if (length >= 16) scalar = *(const __uint128_t *)bytes; +#else + else if (length >= 16) scalar = *(const __uint64_t *)bytes; +#endif + } + return scalar; + } + }; + + Value(); + Value(const Scalar& scalar); + Value(const Vector& vector); + Value(const uint8_t *bytes, int len); + Value(const Value &rhs); + + Value & + operator=(const Value &rhs); + + const ClangASTType & + GetClangType(); + + void + SetClangType (const ClangASTType &clang_type); + + ValueType + GetValueType() const; + + AddressType + GetValueAddressType () const; + + ContextType + GetContextType() const + { + return m_context_type; + } + + void + SetValueType (ValueType value_type) + { + m_value_type = value_type; + } + + void + ClearContext () + { + m_context = NULL; + m_context_type = eContextTypeInvalid; + } + + void + SetContext (ContextType context_type, void *p) + { + m_context_type = context_type; + m_context = p; + if (m_context_type == eContextTypeRegisterInfo) { + RegisterInfo *reg_info = GetRegisterInfo(); + if (reg_info->encoding == lldb::eEncodingVector) + SetValueType(eValueTypeVector); + else + SetValueType(eValueTypeScalar); + } + } + + RegisterInfo * + GetRegisterInfo() const; + + Type * + GetType(); + + Scalar & + ResolveValue (ExecutionContext *exe_ctx); + + const Scalar & + GetScalar() const + { + return m_value; + } + + const Vector & + GetVector() const + { + return m_vector; + } + + Scalar & + GetScalar() + { + return m_value; + } + + Vector & + GetVector() + { + return m_vector; + } + + bool + SetVectorBytes(const Vector& vector) + { + m_vector = vector; + return m_vector.IsValid(); + } + + bool + SetVectorBytes(uint8_t *bytes, size_t length, lldb::ByteOrder byte_order) + { + return m_vector.SetBytes(bytes, length, byte_order); + } + + bool + SetScalarFromVector() + { + if (m_vector.IsValid()) + { + m_value = m_vector.GetAsScalar(); + return true; + } + return false; + } + + void + ResizeData(size_t len); + + bool + ValueOf(ExecutionContext *exe_ctx); + + Variable * + GetVariable(); + + void + Dump (Stream* strm); + + lldb::Format + GetValueDefaultFormat (); + + uint64_t + GetValueByteSize (Error *error_ptr); + + Error + GetValueAsData (ExecutionContext *exe_ctx, + DataExtractor &data, + uint32_t data_offset, + Module *module); // Can be NULL + + static const char * + GetValueTypeAsCString (ValueType context_type); + + static const char * + GetContextTypeAsCString (ContextType context_type); + + bool + GetData (DataExtractor &data); + + void + Clear(); + +protected: + Scalar m_value; + Vector m_vector; + ClangASTType m_clang_type; + void * m_context; + ValueType m_value_type; + ContextType m_context_type; + DataBufferHeap m_data_buffer; +}; + +class ValueList +{ +public: + ValueList () : + m_values() + { + } + + ValueList (const ValueList &rhs); + + ~ValueList () + { + } + + const ValueList & operator= (const ValueList &rhs); + + // void InsertValue (Value *value, size_t idx); + void PushValue (const Value &value); + + size_t GetSize (); + Value *GetValueAtIndex(size_t idx); + void Clear(); + +protected: + +private: + typedef std::vector collection; + + collection m_values; +}; + +} // namespace lldb_private + +#endif // liblldb_Value_h_ diff --git a/include/lldb/Core/ValueObject.h b/include/lldb/Core/ValueObject.h new file mode 100644 index 00000000000..0d965d6ccc0 --- /dev/null +++ b/include/lldb/Core/ValueObject.h @@ -0,0 +1,1375 @@ +//===-- ValueObject.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObject_h_ +#define liblldb_ValueObject_h_ + +// C Includes +// C++ Includes +#include +#include +#include +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/Value.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackID.h" +#include "lldb/Utility/SharedCluster.h" + +namespace lldb_private { + +/// ValueObject: +/// +/// This abstract class provides an interface to a particular value, be it a register, a local or global variable, +/// that is evaluated in some particular scope. The ValueObject also has the capibility of being the "child" of +/// some other variable object, and in turn of having children. +/// If a ValueObject is a root variable object - having no parent - then it must be constructed with respect to some +/// particular ExecutionContextScope. If it is a child, it inherits the ExecutionContextScope from its parent. +/// The ValueObject will update itself if necessary before fetching its value, summary, object description, etc. +/// But it will always update itself in the ExecutionContextScope with which it was originally created. + +/// A brief note on life cycle management for ValueObjects. This is a little tricky because a ValueObject can contain +/// various other ValueObjects - the Dynamic Value, its children, the dereference value, etc. Any one of these can be +/// handed out as a shared pointer, but for that contained value object to be valid, the root object and potentially other +/// of the value objects need to stay around. +/// We solve this problem by handing out shared pointers to the Value Object and any of its dependents using a shared +/// ClusterManager. This treats each shared pointer handed out for the entire cluster as a reference to the whole +/// cluster. The whole cluster will stay around until the last reference is released. +/// +/// The ValueObject mostly handle this automatically, if a value object is made with a Parent ValueObject, then it adds +/// itself to the ClusterManager of the parent. + +/// It does mean that external to the ValueObjects we should only ever make available ValueObjectSP's, never ValueObjects +/// or pointers to them. So all the "Root level" ValueObject derived constructors should be private, and +/// should implement a Create function that new's up object and returns a Shared Pointer that it gets from the GetSP() method. +/// +/// However, if you are making an derived ValueObject that will be contained in a parent value object, you should just +/// hold onto a pointer to it internally, and by virtue of passing the parent ValueObject into its constructor, it will +/// be added to the ClusterManager for the parent. Then if you ever hand out a Shared Pointer to the contained ValueObject, +/// just do so by calling GetSP() on the contained object. + +class ValueObject : public UserID +{ +public: + + enum GetExpressionPathFormat + { + eGetExpressionPathFormatDereferencePointers = 1, + eGetExpressionPathFormatHonorPointers + }; + + enum ValueObjectRepresentationStyle + { + eValueObjectRepresentationStyleValue = 1, + eValueObjectRepresentationStyleSummary, + eValueObjectRepresentationStyleLanguageSpecific, + eValueObjectRepresentationStyleLocation, + eValueObjectRepresentationStyleChildrenCount, + eValueObjectRepresentationStyleType, + eValueObjectRepresentationStyleName, + eValueObjectRepresentationStyleExpressionPath + }; + + enum ExpressionPathScanEndReason + { + eExpressionPathScanEndReasonEndOfString = 1, // out of data to parse + eExpressionPathScanEndReasonNoSuchChild, // child element not found + eExpressionPathScanEndReasonEmptyRangeNotAllowed, // [] only allowed for arrays + eExpressionPathScanEndReasonDotInsteadOfArrow, // . used when -> should be used + eExpressionPathScanEndReasonArrowInsteadOfDot, // -> used when . should be used + eExpressionPathScanEndReasonFragileIVarNotAllowed, // ObjC ivar expansion not allowed + eExpressionPathScanEndReasonRangeOperatorNotAllowed, // [] not allowed by options + eExpressionPathScanEndReasonRangeOperatorInvalid, // [] not valid on objects other than scalars, pointers or arrays + eExpressionPathScanEndReasonArrayRangeOperatorMet, // [] is good for arrays, but I cannot parse it + eExpressionPathScanEndReasonBitfieldRangeOperatorMet, // [] is good for bitfields, but I cannot parse after it + eExpressionPathScanEndReasonUnexpectedSymbol, // something is malformed in the expression + eExpressionPathScanEndReasonTakingAddressFailed, // impossible to apply & operator + eExpressionPathScanEndReasonDereferencingFailed, // impossible to apply * operator + eExpressionPathScanEndReasonRangeOperatorExpanded, // [] was expanded into a VOList + eExpressionPathScanEndReasonSyntheticValueMissing, // getting the synthetic children failed + eExpressionPathScanEndReasonUnknown = 0xFFFF + }; + + enum ExpressionPathEndResultType + { + eExpressionPathEndResultTypePlain = 1, // anything but... + eExpressionPathEndResultTypeBitfield, // a bitfield + eExpressionPathEndResultTypeBoundedRange, // a range [low-high] + eExpressionPathEndResultTypeUnboundedRange, // a range [] + eExpressionPathEndResultTypeValueObjectList, // several items in a VOList + eExpressionPathEndResultTypeInvalid = 0xFFFF + }; + + enum ExpressionPathAftermath + { + eExpressionPathAftermathNothing = 1, // just return it + eExpressionPathAftermathDereference, // dereference the target + eExpressionPathAftermathTakeAddress // take target's address + }; + + enum ClearUserVisibleDataItems + { + eClearUserVisibleDataItemsNothing = 1u << 0, + eClearUserVisibleDataItemsValue = 1u << 1, + eClearUserVisibleDataItemsSummary = 1u << 2, + eClearUserVisibleDataItemsLocation = 1u << 3, + eClearUserVisibleDataItemsDescription = 1u << 4, + eClearUserVisibleDataItemsSyntheticChildren = 1u << 5, + eClearUserVisibleDataItemsAllStrings = eClearUserVisibleDataItemsValue | eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsLocation | eClearUserVisibleDataItemsDescription, + eClearUserVisibleDataItemsAll = 0xFFFF + }; + + struct GetValueForExpressionPathOptions + { + bool m_check_dot_vs_arrow_syntax; + bool m_no_fragile_ivar; + bool m_allow_bitfields_syntax; + bool m_no_synthetic_children; + + GetValueForExpressionPathOptions(bool dot = false, + bool no_ivar = false, + bool bitfield = true, + bool no_synth = false) : + m_check_dot_vs_arrow_syntax(dot), + m_no_fragile_ivar(no_ivar), + m_allow_bitfields_syntax(bitfield), + m_no_synthetic_children(no_synth) + { + } + + GetValueForExpressionPathOptions& + DoCheckDotVsArrowSyntax() + { + m_check_dot_vs_arrow_syntax = true; + return *this; + } + + GetValueForExpressionPathOptions& + DontCheckDotVsArrowSyntax() + { + m_check_dot_vs_arrow_syntax = false; + return *this; + } + + GetValueForExpressionPathOptions& + DoAllowFragileIVar() + { + m_no_fragile_ivar = false; + return *this; + } + + GetValueForExpressionPathOptions& + DontAllowFragileIVar() + { + m_no_fragile_ivar = true; + return *this; + } + + GetValueForExpressionPathOptions& + DoAllowBitfieldSyntax() + { + m_allow_bitfields_syntax = true; + return *this; + } + + GetValueForExpressionPathOptions& + DontAllowBitfieldSyntax() + { + m_allow_bitfields_syntax = false; + return *this; + } + + GetValueForExpressionPathOptions& + DoAllowSyntheticChildren() + { + m_no_synthetic_children = false; + return *this; + } + + GetValueForExpressionPathOptions& + DontAllowSyntheticChildren() + { + m_no_synthetic_children = true; + return *this; + } + + static const GetValueForExpressionPathOptions + DefaultOptions() + { + static GetValueForExpressionPathOptions g_default_options; + + return g_default_options; + } + + }; + + struct DumpValueObjectOptions + { + uint32_t m_max_ptr_depth; + uint32_t m_max_depth; + bool m_show_types; + bool m_show_location; + bool m_use_objc; + lldb::DynamicValueType m_use_dynamic; + bool m_use_synthetic; + bool m_scope_already_checked; + bool m_flat_output; + uint32_t m_omit_summary_depth; + bool m_ignore_cap; + lldb::Format m_format; + lldb::TypeSummaryImplSP m_summary_sp; + std::string m_root_valobj_name; + bool m_hide_root_type; + bool m_hide_name; + bool m_hide_value; + + DumpValueObjectOptions() : + m_max_ptr_depth(0), + m_max_depth(UINT32_MAX), + m_show_types(false), + m_show_location(false), + m_use_objc(false), + m_use_dynamic(lldb::eNoDynamicValues), + m_use_synthetic(true), + m_scope_already_checked(false), + m_flat_output(false), + m_omit_summary_depth(0), + m_ignore_cap(false), + m_format (lldb::eFormatDefault), + m_summary_sp(), + m_root_valobj_name(), + m_hide_root_type(false), // provide a special compact display for "po" + m_hide_name(false), // provide a special compact display for "po" + m_hide_value(false) // provide a special compact display for "po" + {} + + static const DumpValueObjectOptions + DefaultOptions() + { + static DumpValueObjectOptions g_default_options; + + return g_default_options; + } + + DumpValueObjectOptions (const DumpValueObjectOptions& rhs) : + m_max_ptr_depth(rhs.m_max_ptr_depth), + m_max_depth(rhs.m_max_depth), + m_show_types(rhs.m_show_types), + m_show_location(rhs.m_show_location), + m_use_objc(rhs.m_use_objc), + m_use_dynamic(rhs.m_use_dynamic), + m_use_synthetic(rhs.m_use_synthetic), + m_scope_already_checked(rhs.m_scope_already_checked), + m_flat_output(rhs.m_flat_output), + m_omit_summary_depth(rhs.m_omit_summary_depth), + m_ignore_cap(rhs.m_ignore_cap), + m_format(rhs.m_format), + m_summary_sp(rhs.m_summary_sp), + m_root_valobj_name(rhs.m_root_valobj_name), + m_hide_root_type(rhs.m_hide_root_type), + m_hide_name(rhs.m_hide_name), + m_hide_value(rhs.m_hide_value) + {} + + DumpValueObjectOptions& + SetMaximumPointerDepth(uint32_t depth = 0) + { + m_max_ptr_depth = depth; + return *this; + } + + DumpValueObjectOptions& + SetMaximumDepth(uint32_t depth = 0) + { + m_max_depth = depth; + return *this; + } + + DumpValueObjectOptions& + SetShowTypes(bool show = false) + { + m_show_types = show; + return *this; + } + + DumpValueObjectOptions& + SetShowLocation(bool show = false) + { + m_show_location = show; + return *this; + } + + DumpValueObjectOptions& + SetUseObjectiveC(bool use = false) + { + m_use_objc = use; + return *this; + } + + DumpValueObjectOptions& + SetShowSummary(bool show = true) + { + if (show == false) + SetOmitSummaryDepth(UINT32_MAX); + else + SetOmitSummaryDepth(0); + return *this; + } + + DumpValueObjectOptions& + SetUseDynamicType(lldb::DynamicValueType dyn = lldb::eNoDynamicValues) + { + m_use_dynamic = dyn; + return *this; + } + + DumpValueObjectOptions& + SetUseSyntheticValue(bool use_synthetic = true) + { + m_use_synthetic = use_synthetic; + return *this; + } + + DumpValueObjectOptions& + SetScopeChecked(bool check = true) + { + m_scope_already_checked = check; + return *this; + } + + DumpValueObjectOptions& + SetFlatOutput(bool flat = false) + { + m_flat_output = flat; + return *this; + } + + DumpValueObjectOptions& + SetOmitSummaryDepth(uint32_t depth = 0) + { + m_omit_summary_depth = depth; + return *this; + } + + DumpValueObjectOptions& + SetIgnoreCap(bool ignore = false) + { + m_ignore_cap = ignore; + return *this; + } + + DumpValueObjectOptions& + SetRawDisplay(bool raw = false) + { + if (raw) + { + SetUseSyntheticValue(false); + SetOmitSummaryDepth(UINT32_MAX); + SetIgnoreCap(true); + SetHideName(false); + SetHideValue(false); + } + else + { + SetUseSyntheticValue(true); + SetOmitSummaryDepth(0); + SetIgnoreCap(false); + SetHideName(false); + SetHideValue(false); + } + return *this; + } + + DumpValueObjectOptions& + SetFormat (lldb::Format format = lldb::eFormatDefault) + { + m_format = format; + return *this; + } + + DumpValueObjectOptions& + SetSummary (lldb::TypeSummaryImplSP summary = lldb::TypeSummaryImplSP()) + { + m_summary_sp = summary; + return *this; + } + + DumpValueObjectOptions& + SetRootValueObjectName (const char* name = NULL) + { + if (name) + m_root_valobj_name.assign(name); + else + m_root_valobj_name.clear(); + return *this; + } + + DumpValueObjectOptions& + SetHideRootType (bool hide_root_type = false) + { + m_hide_root_type = hide_root_type; + return *this; + } + + DumpValueObjectOptions& + SetHideName (bool hide_name = false) + { + m_hide_name = hide_name; + return *this; + } + + DumpValueObjectOptions& + SetHideValue (bool hide_value = false) + { + m_hide_value = hide_value; + return *this; + } + }; + + class EvaluationPoint + { + public: + + EvaluationPoint (); + + EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected = false); + + EvaluationPoint (const EvaluationPoint &rhs); + + ~EvaluationPoint (); + + const ExecutionContextRef & + GetExecutionContextRef() const + { + return m_exe_ctx_ref; + } + + // Set the EvaluationPoint to the values in exe_scope, + // Return true if the Evaluation Point changed. + // Since the ExecutionContextScope is always going to be valid currently, + // the Updated Context will also always be valid. + +// bool +// SetContext (ExecutionContextScope *exe_scope); + + void + SetIsConstant () + { + SetUpdated(); + m_mod_id.SetInvalid(); + } + + bool + IsConstant () const + { + return !m_mod_id.IsValid(); + } + + ProcessModID + GetModID () const + { + return m_mod_id; + } + + void + SetUpdateID (ProcessModID new_id) + { + m_mod_id = new_id; + } + + bool + IsFirstEvaluation () const + { + return m_first_update; + } + + void + SetNeedsUpdate () + { + m_needs_update = true; + } + + void + SetUpdated (); + + bool + NeedsUpdating() + { + SyncWithProcessState(); + return m_needs_update; + } + + bool + IsValid () + { + if (!m_mod_id.IsValid()) + return false; + else if (SyncWithProcessState ()) + { + if (!m_mod_id.IsValid()) + return false; + } + return true; + } + + void + SetInvalid () + { + // Use the stop id to mark us as invalid, leave the thread id and the stack id around for logging and + // history purposes. + m_mod_id.SetInvalid(); + + // Can't update an invalid state. + m_needs_update = false; + + } + + private: + bool + SyncWithProcessState (); + + ProcessModID m_mod_id; // This is the stop id when this ValueObject was last evaluated. + ExecutionContextRef m_exe_ctx_ref; + bool m_needs_update; + bool m_first_update; + }; + + const EvaluationPoint & + GetUpdatePoint () const + { + return m_update_point; + } + + EvaluationPoint & + GetUpdatePoint () + { + return m_update_point; + } + + const ExecutionContextRef & + GetExecutionContextRef() const + { + return m_update_point.GetExecutionContextRef(); + } + + lldb::TargetSP + GetTargetSP() const + { + return m_update_point.GetExecutionContextRef().GetTargetSP(); + } + + lldb::ProcessSP + GetProcessSP() const + { + return m_update_point.GetExecutionContextRef().GetProcessSP(); + } + + lldb::ThreadSP + GetThreadSP() const + { + return m_update_point.GetExecutionContextRef().GetThreadSP(); + } + + lldb::StackFrameSP + GetFrameSP() const + { + return m_update_point.GetExecutionContextRef().GetFrameSP(); + } + + void + SetNeedsUpdate (); + + virtual ~ValueObject(); + + ClangASTType + GetClangType (); + + //------------------------------------------------------------------ + // Sublasses must implement the functions below. + //------------------------------------------------------------------ + virtual uint64_t + GetByteSize() = 0; + + virtual lldb::ValueType + GetValueType() const = 0; + + //------------------------------------------------------------------ + // Sublasses can implement the functions below. + //------------------------------------------------------------------ + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual lldb::LanguageType + GetObjectRuntimeLanguage(); + + virtual uint32_t + GetTypeInfo (ClangASTType *pointee_or_element_clang_type = NULL); + + virtual bool + IsPointerType (); + + virtual bool + IsArrayType (); + + virtual bool + IsScalarType (); + + virtual bool + IsPointerOrReferenceType (); + + virtual bool + IsPossibleDynamicType (); + + virtual bool + IsObjCNil (); + + virtual bool + IsBaseClass () + { + return false; + } + + virtual bool + IsDereferenceOfParent () + { + return false; + } + + bool + IsIntegerType (bool &is_signed); + + virtual bool + GetBaseClassPath (Stream &s); + + virtual void + GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat = eGetExpressionPathFormatDereferencePointers); + + lldb::ValueObjectSP + GetValueForExpressionPath(const char* expression, + const char** first_unparsed = NULL, + ExpressionPathScanEndReason* reason_to_stop = NULL, + ExpressionPathEndResultType* final_value_type = NULL, + const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(), + ExpressionPathAftermath* final_task_on_target = NULL); + + int + GetValuesForExpressionPath(const char* expression, + lldb::ValueObjectListSP& list, + const char** first_unparsed = NULL, + ExpressionPathScanEndReason* reason_to_stop = NULL, + ExpressionPathEndResultType* final_value_type = NULL, + const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(), + ExpressionPathAftermath* final_task_on_target = NULL); + + virtual bool + IsInScope () + { + return true; + } + + virtual off_t + GetByteOffset() + { + return 0; + } + + virtual uint32_t + GetBitfieldBitSize () + { + return 0; + } + + virtual uint32_t + GetBitfieldBitOffset () + { + return 0; + } + + bool + IsBitfield () + { + return (GetBitfieldBitSize() != 0) || (GetBitfieldBitOffset() != 0); + } + + virtual bool + IsArrayItemForPointer() + { + return m_is_array_item_for_pointer; + } + + virtual const char * + GetValueAsCString (); + + virtual bool + GetValueAsCString (lldb::Format format, + std::string& destination); + + virtual uint64_t + GetValueAsUnsigned (uint64_t fail_value, bool *success = NULL); + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + // Return the module associated with this value object in case the + // value is from an executable file and might have its data in + // sections of the file. This can be used for variables. + virtual lldb::ModuleSP + GetModule(); + + virtual ValueObject* + GetRoot (); + + virtual bool + GetDeclaration (Declaration &decl); + + //------------------------------------------------------------------ + // The functions below should NOT be modified by sublasses + //------------------------------------------------------------------ + const Error & + GetError(); + + const ConstString & + GetName() const; + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx, bool can_create); + + // this will always create the children if necessary + lldb::ValueObjectSP + GetChildAtIndexPath (const std::initializer_list &idxs, + size_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::vector &idxs, + size_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::initializer_list< std::pair > &idxs, + size_t* index_of_error = NULL); + + lldb::ValueObjectSP + GetChildAtIndexPath (const std::vector< std::pair > &idxs, + size_t* index_of_error = NULL); + + virtual lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + size_t + GetNumChildren (); + + const Value & + GetValue() const; + + Value & + GetValue(); + + virtual bool + ResolveValue (Scalar &scalar); + + virtual const char * + GetLocationAsCString (); + + const char * + GetSummaryAsCString (); + + bool + GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination); + + const char * + GetObjectDescription (); + + bool + HasSpecialPrintableRepresentation (ValueObjectRepresentationStyle val_obj_display, + lldb::Format custom_format); + + enum PrintableRepresentationSpecialCases + { + ePrintableRepresentationSpecialCasesDisable = 0, + ePrintableRepresentationSpecialCasesAllow = 1, + ePrintableRepresentationSpecialCasesOnly = 3 + }; + + bool + DumpPrintableRepresentation (Stream& s, + ValueObjectRepresentationStyle val_obj_display = eValueObjectRepresentationStyleSummary, + lldb::Format custom_format = lldb::eFormatInvalid, + PrintableRepresentationSpecialCases special = ePrintableRepresentationSpecialCasesAllow); + bool + GetValueIsValid () const; + + bool + GetValueDidChange (); + + bool + UpdateValueIfNeeded (bool update_format = true); + + bool + UpdateFormatsIfNeeded(); + + lldb::ValueObjectSP + GetSP () + { + return m_manager->GetSharedPointer(this); + } + + void + SetName (const ConstString &name); + + virtual lldb::addr_t + GetAddressOf (bool scalar_is_load_address = true, + AddressType *address_type = NULL); + + lldb::addr_t + GetPointerValue (AddressType *address_type = NULL); + + lldb::ValueObjectSP + GetSyntheticChild (const ConstString &key) const; + + lldb::ValueObjectSP + GetSyntheticArrayMember (size_t index, bool can_create); + + lldb::ValueObjectSP + GetSyntheticArrayMemberFromPointer (size_t index, bool can_create); + + lldb::ValueObjectSP + GetSyntheticArrayMemberFromArray (size_t index, bool can_create); + + lldb::ValueObjectSP + GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create); + + lldb::ValueObjectSP + GetSyntheticExpressionPathChild(const char* expression, bool can_create); + + virtual lldb::ValueObjectSP + GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create); + + virtual lldb::ValueObjectSP + GetDynamicValue (lldb::DynamicValueType valueType); + + lldb::DynamicValueType + GetDynamicValueType (); + + virtual lldb::ValueObjectSP + GetStaticValue (); + + virtual lldb::ValueObjectSP + GetNonSyntheticValue (); + + lldb::ValueObjectSP + GetSyntheticValue (bool use_synthetic = true); + + virtual bool + HasSyntheticValue(); + + virtual bool + IsSynthetic() { return false; } + + virtual lldb::ValueObjectSP + CreateConstantValue (const ConstString &name); + + virtual lldb::ValueObjectSP + Dereference (Error &error); + + virtual lldb::ValueObjectSP + AddressOf (Error &error); + + virtual lldb::addr_t + GetLiveAddress() + { + return LLDB_INVALID_ADDRESS; + } + + virtual void + SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, + AddressType address_type = eAddressTypeLoad) + { + } + + virtual lldb::ValueObjectSP + Cast (const ClangASTType &clang_ast_type); + + virtual lldb::ValueObjectSP + CastPointerType (const char *name, + ClangASTType &ast_type); + + virtual lldb::ValueObjectSP + CastPointerType (const char *name, + lldb::TypeSP &type_sp); + + // The backing bits of this value object were updated, clear any + // descriptive string, so we know we have to refetch them + virtual void + ValueUpdated () + { + ClearUserVisibleData(eClearUserVisibleDataItemsValue | + eClearUserVisibleDataItemsSummary | + eClearUserVisibleDataItemsDescription); + } + + virtual bool + IsDynamic () + { + return false; + } + + virtual SymbolContextScope * + GetSymbolContextScope(); + + static void + DumpValueObject (Stream &s, + ValueObject *valobj); + static void + DumpValueObject (Stream &s, + ValueObject *valobj, + const DumpValueObjectOptions& options); + + static lldb::ValueObjectSP + CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx); + + static lldb::ValueObjectSP + CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type); + + static lldb::ValueObjectSP + CreateValueObjectFromData (const char* name, + DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType type); + + static void + LogValueObject (Log *log, + ValueObject *valobj); + + static void + LogValueObject (Log *log, + ValueObject *valobj, + const DumpValueObjectOptions& options); + + + // returns true if this is a char* or a char[] + // if it is a char* and check_pointer is true, + // it also checks that the pointer is valid + bool + IsCStringContainer (bool check_pointer = false); + + size_t + ReadPointedString (Stream& s, + Error& error, + uint32_t max_length = 0, + bool honor_array = true, + lldb::Format item_format = lldb::eFormatCharArray); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + + virtual uint64_t + GetData (DataExtractor& data); + + virtual bool + SetData (DataExtractor &data, Error &error); + + bool + GetIsConstant () const + { + return m_update_point.IsConstant(); + } + + void + SetIsConstant () + { + m_update_point.SetIsConstant(); + } + + lldb::Format + GetFormat () const; + + void + SetFormat (lldb::Format format) + { + if (format != m_format) + ClearUserVisibleData(eClearUserVisibleDataItemsValue); + m_format = format; + } + + lldb::TypeSummaryImplSP + GetSummaryFormat() + { + UpdateFormatsIfNeeded(); + return m_type_summary_sp; + } + + void + SetSummaryFormat(lldb::TypeSummaryImplSP format) + { + m_type_summary_sp = format; + ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + } + + void + SetValueFormat(lldb::TypeFormatImplSP format) + { + m_type_format_sp = format; + ClearUserVisibleData(eClearUserVisibleDataItemsValue); + } + + lldb::TypeFormatImplSP + GetValueFormat() + { + UpdateFormatsIfNeeded(); + return m_type_format_sp; + } + + void + SetSyntheticChildren(const lldb::SyntheticChildrenSP &synth_sp) + { + if (synth_sp.get() == m_synthetic_children_sp.get()) + return; + ClearUserVisibleData(eClearUserVisibleDataItemsSyntheticChildren); + m_synthetic_children_sp = synth_sp; + } + + lldb::SyntheticChildrenSP + GetSyntheticChildren() + { + UpdateFormatsIfNeeded(); + return m_synthetic_children_sp; + } + + // Use GetParent for display purposes, but if you want to tell the parent to update itself + // then use m_parent. The ValueObjectDynamicValue's parent is not the correct parent for + // displaying, they are really siblings, so for display it needs to route through to its grandparent. + virtual ValueObject * + GetParent() + { + return m_parent; + } + + virtual const ValueObject * + GetParent() const + { + return m_parent; + } + + ValueObject * + GetNonBaseClassParent(); + + void + SetAddressTypeOfChildren(AddressType at) + { + m_address_type_of_ptr_or_ref_children = at; + } + + AddressType + GetAddressTypeOfChildren(); + + void + SetHasCompleteType() + { + m_did_calculate_complete_objc_class_type = true; + } + + //------------------------------------------------------------------ + /// Find out if a ValueObject might have children. + /// + /// This call is much more efficient than CalculateNumChildren() as + /// it doesn't need to complete the underlying type. This is designed + /// to be used in a UI environment in order to detect if the + /// disclosure triangle should be displayed or not. + /// + /// This function returns true for class, union, structure, + /// pointers, references, arrays and more. Again, it does so without + /// doing any expensive type completion. + /// + /// @return + /// Returns \b true if the ValueObject might have children, or \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + MightHaveChildren(); + +protected: + typedef ClusterManager ValueObjectManager; + + class ChildrenManager + { + public: + ChildrenManager() : + m_mutex(Mutex::eMutexTypeRecursive), + m_children(), + m_children_count(0) + {} + + bool + HasChildAtIndex (size_t idx) + { + Mutex::Locker locker(m_mutex); + ChildrenIterator iter = m_children.find(idx); + ChildrenIterator end = m_children.end(); + return (iter != end); + } + + ValueObject* + GetChildAtIndex (size_t idx) + { + Mutex::Locker locker(m_mutex); + ChildrenIterator iter = m_children.find(idx); + ChildrenIterator end = m_children.end(); + if (iter == end) + return NULL; + else + return iter->second; + } + + void + SetChildAtIndex (size_t idx, ValueObject* valobj) + { + ChildrenPair pair(idx,valobj); // we do not need to be mutex-protected to make a pair + Mutex::Locker locker(m_mutex); + m_children.insert(pair); + } + + void + SetChildrenCount (size_t count) + { + m_children_count = count; + } + + size_t + GetChildrenCount () + { + return m_children_count; + } + + void + Clear() + { + m_children_count = 0; + Mutex::Locker locker(m_mutex); + m_children.clear(); + } + + private: + typedef std::map ChildrenMap; + typedef ChildrenMap::iterator ChildrenIterator; + typedef ChildrenMap::value_type ChildrenPair; + Mutex m_mutex; + ChildrenMap m_children; + size_t m_children_count; + }; + + //------------------------------------------------------------------ + // Classes that inherit from ValueObject can see and modify these + //------------------------------------------------------------------ + ValueObject * m_parent; // The parent value object, or NULL if this has no parent + ValueObject * m_root; // The root of the hierarchy for this ValueObject (or NULL if never calculated) + EvaluationPoint m_update_point; // Stores both the stop id and the full context at which this value was last + // updated. When we are asked to update the value object, we check whether + // the context & stop id are the same before updating. + ConstString m_name; // The name of this object + DataExtractor m_data; // A data extractor that can be used to extract the value. + Value m_value; + Error m_error; // An error object that can describe any errors that occur when updating values. + std::string m_value_str; // Cached value string that will get cleared if/when the value is updated. + std::string m_old_value_str;// Cached old value string from the last time the value was gotten + std::string m_location_str; // Cached location string that will get cleared if/when the value is updated. + std::string m_summary_str; // Cached summary string that will get cleared if/when the value is updated. + std::string m_object_desc_str; // Cached result of the "object printer". This differs from the summary + // in that the summary is consed up by us, the object_desc_string is builtin. + + ClangASTType m_override_type;// If the type of the value object should be overridden, the type to impose. + + ValueObjectManager *m_manager; // This object is managed by the root object (any ValueObject that gets created + // without a parent.) The manager gets passed through all the generations of + // dependent objects, and will keep the whole cluster of objects alive as long + // as a shared pointer to any of them has been handed out. Shared pointers to + // value objects must always be made with the GetSP method. + + ChildrenManager m_children; + std::map m_synthetic_children; + + ValueObject* m_dynamic_value; + ValueObject* m_synthetic_value; + ValueObject* m_deref_valobj; + + lldb::ValueObjectSP m_addr_of_valobj_sp; // We have to hold onto a shared pointer to this one because it is created + // as an independent ValueObjectConstResult, which isn't managed by us. + + lldb::Format m_format; + lldb::Format m_last_format; + uint32_t m_last_format_mgr_revision; + lldb::TypeSummaryImplSP m_type_summary_sp; + lldb::TypeFormatImplSP m_type_format_sp; + lldb::SyntheticChildrenSP m_synthetic_children_sp; + ProcessModID m_user_id_of_forced_summary; + AddressType m_address_type_of_ptr_or_ref_children; + + bool m_value_is_valid:1, + m_value_did_change:1, + m_children_count_valid:1, + m_old_value_valid:1, + m_is_deref_of_parent:1, + m_is_array_item_for_pointer:1, + m_is_bitfield_for_scalar:1, + m_is_child_at_offset:1, + m_is_getting_summary:1, + m_did_calculate_complete_objc_class_type:1; + + friend class ClangExpressionDeclMap; // For GetValue + friend class ClangExpressionVariable; // For SetName + friend class Target; // For SetName + friend class ValueObjectConstResultImpl; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + + // Use the no-argument constructor to make a constant variable object (with no ExecutionContextScope.) + + ValueObject(); + + // Use this constructor to create a "root variable object". The ValueObject will be locked to this context + // through-out its lifespan. + + ValueObject (ExecutionContextScope *exe_scope, + AddressType child_ptr_or_ref_addr_type = eAddressTypeLoad); + + // Use this constructor to create a ValueObject owned by another ValueObject. It will inherit the ExecutionContext + // of its parent. + + ValueObject (ValueObject &parent); + + ValueObjectManager * + GetManager() + { + return m_manager; + } + + virtual bool + UpdateValue () = 0; + + virtual void + CalculateDynamicValue (lldb::DynamicValueType use_dynamic); + + virtual lldb::DynamicValueType + GetDynamicValueTypeImpl () + { + return lldb::eNoDynamicValues; + } + + virtual bool + HasDynamicValueTypeInfo () + { + return false; + } + + virtual void + CalculateSyntheticValue (bool use_synthetic = true); + + // Should only be called by ValueObject::GetChildAtIndex() + // Returns a ValueObject managed by this ValueObject's manager. + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + // Should only be called by ValueObject::GetNumChildren() + virtual size_t + CalculateNumChildren() = 0; + + void + SetNumChildren (size_t num_children); + + void + SetValueDidChange (bool value_changed); + + void + SetValueIsValid (bool valid); + + void + ClearUserVisibleData(uint32_t items = ValueObject::eClearUserVisibleDataItemsAllStrings); + + void + AddSyntheticChild (const ConstString &key, + ValueObject *valobj); + + DataExtractor & + GetDataExtractor (); + + void + ClearDynamicTypeInformation (); + + //------------------------------------------------------------------ + // Sublasses must implement the functions below. + //------------------------------------------------------------------ + + virtual ClangASTType + GetClangTypeImpl () = 0; + + const char * + GetLocationAsCStringImpl (const Value& value, + const DataExtractor& data); + +private: + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + + virtual ClangASTType + MaybeCalculateCompleteType (); + + lldb::ValueObjectSP + GetValueForExpressionPath_Impl(const char* expression_cstr, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target); + + // this method will ONLY expand [] expressions into a VOList and return + // the number of elements it added to the VOList + // it will NOT loop through expanding the follow-up of the expression_cstr + // for all objects in the list + int + ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + lldb::ValueObjectSP root, + lldb::ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target); + + + DISALLOW_COPY_AND_ASSIGN (ValueObject); + +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObject_h_ diff --git a/include/lldb/Core/ValueObjectCast.h b/include/lldb/Core/ValueObjectCast.h new file mode 100644 index 00000000000..1538d7a5563 --- /dev/null +++ b/include/lldb/Core/ValueObjectCast.h @@ -0,0 +1,87 @@ +//===-- ValueObjectDynamicValue.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectCast_h_ +#define liblldb_ValueObjectCast_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//--------------------------------------------------------------------------------- +// A ValueObject that represents a given value represented as a different type. +//--------------------------------------------------------------------------------- +class ValueObjectCast : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type); + + virtual + ~ValueObjectCast(); + + virtual uint64_t + GetByteSize(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual ValueObject * + GetParent() + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual const ValueObject * + GetParent() const + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + ClangASTType m_cast_type; + +private: + ValueObjectCast (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectCast); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectCast_h_ diff --git a/include/lldb/Core/ValueObjectChild.h b/include/lldb/Core/ValueObjectChild.h new file mode 100644 index 00000000000..780529a4af1 --- /dev/null +++ b/include/lldb/Core/ValueObjectChild.h @@ -0,0 +1,122 @@ +//===-- ValueObjectChild.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectChild_h_ +#define liblldb_ValueObjectChild_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A child of another ValueObject. +//---------------------------------------------------------------------- +class ValueObjectChild : public ValueObject +{ +public: + virtual ~ValueObjectChild(); + + virtual uint64_t + GetByteSize() + { + return m_byte_size; + } + + virtual off_t + GetByteOffset() + { + return m_byte_offset; + } + + virtual uint32_t + GetBitfieldBitSize() + { + return m_bitfield_bit_size; + } + + virtual uint32_t + GetBitfieldBitOffset() + { + return m_bitfield_bit_offset; + } + + virtual lldb::ValueType + GetValueType() const; + + virtual size_t + CalculateNumChildren(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual bool + IsInScope (); + + virtual bool + IsBaseClass () + { + return m_is_base_class; + } + + virtual bool + IsDereferenceOfParent () + { + return m_is_deref_of_parent; + } + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl () + { + return m_clang_type; + } + + ClangASTType m_clang_type; + ConstString m_type_name; + uint64_t m_byte_size; + int32_t m_byte_offset; + uint8_t m_bitfield_bit_size; + uint8_t m_bitfield_bit_offset; + bool m_is_base_class; + bool m_is_deref_of_parent; + +// +// void +// ReadValueFromMemory (ValueObject* parent, lldb::addr_t address); + +protected: + friend class ValueObject; + friend class ValueObjectConstResult; + ValueObjectChild (ValueObject &parent, + const ClangASTType &clang_type, + const 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); + + DISALLOW_COPY_AND_ASSIGN (ValueObjectChild); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectChild_h_ diff --git a/include/lldb/Core/ValueObjectConstResult.h b/include/lldb/Core/ValueObjectConstResult.h new file mode 100644 index 00000000000..4964d0589a0 --- /dev/null +++ b/include/lldb/Core/ValueObjectConstResult.h @@ -0,0 +1,179 @@ +//===-- ValueObjectConstResult.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectConstResult_h_ +#define liblldb_ValueObjectConstResult_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +#include "lldb/Core/ValueObjectConstResultImpl.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A frozen ValueObject copied into host memory +//---------------------------------------------------------------------- +class ValueObjectConstResult : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const lldb::DataBufferSP &result_data_sp, + lldb::ByteOrder byte_order, + uint32_t addr_size, + lldb::addr_t address = LLDB_INVALID_ADDRESS); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + Value &value, + const ConstString &name); + + // When an expression fails to evaluate, we return an error + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const Error& error); + + virtual ~ValueObjectConstResult(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType() const; + + virtual size_t + CalculateNumChildren(); + + virtual ConstString + GetTypeName(); + + virtual bool + IsInScope (); + + void + SetByteSize (size_t size); + + virtual lldb::ValueObjectSP + Dereference (Error &error); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + virtual lldb::ValueObjectSP + GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create); + + virtual lldb::ValueObjectSP + AddressOf (Error &error); + + virtual lldb::addr_t + GetAddressOf (bool scalar_is_load_address = true, + AddressType *address_type = NULL); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + + virtual lldb::addr_t + GetLiveAddress() + { + return m_impl.GetLiveAddress(); + } + + virtual void + SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, + AddressType address_type = eAddressTypeLoad) + { + m_impl.SetLiveAddress(addr, + address_type); + } + + virtual lldb::ValueObjectSP + GetDynamicValue (lldb::DynamicValueType valueType); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + ConstString m_type_name; + uint64_t m_byte_size; + + ValueObjectConstResultImpl m_impl; + +private: + friend class ValueObjectConstResultImpl; + ValueObjectConstResult (ExecutionContextScope *exe_scope, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size, + lldb::addr_t address); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + const lldb::DataBufferSP &result_data_sp, + lldb::ByteOrder byte_order, + uint32_t addr_size, + lldb::addr_t address); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Value &value, + const ConstString &name); + + ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Error& error); + + DISALLOW_COPY_AND_ASSIGN (ValueObjectConstResult); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectConstResult_h_ diff --git a/include/lldb/Core/ValueObjectConstResultChild.h b/include/lldb/Core/ValueObjectConstResultChild.h new file mode 100644 index 00000000000..9063276b019 --- /dev/null +++ b/include/lldb/Core/ValueObjectConstResultChild.h @@ -0,0 +1,77 @@ +//===-- ValueObjectConstResultChild.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectConstResultChild_h_ +#define liblldb_ValueObjectConstResultChild_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResultImpl.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A child of a ValueObjectConstResult. +//---------------------------------------------------------------------- +class ValueObjectConstResultChild : public ValueObjectChild +{ +public: + + ValueObjectConstResultChild (ValueObject &parent, + const ClangASTType &clang_type, + const 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); + + virtual ~ValueObjectConstResultChild(); + + virtual lldb::ValueObjectSP + Dereference (Error &error); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + virtual ClangASTType + GetClangType () + { + return ValueObjectChild::GetClangType(); + } + + virtual lldb::ValueObjectSP + GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create); + + virtual lldb::ValueObjectSP + AddressOf (Error &error); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + +protected: + ValueObjectConstResultImpl m_impl; + +private: + friend class ValueObject; + friend class ValueObjectConstResult; + friend class ValueObjectConstResultImpl; + + DISALLOW_COPY_AND_ASSIGN (ValueObjectConstResultChild); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectConstResultChild_h_ diff --git a/include/lldb/Core/ValueObjectConstResultImpl.h b/include/lldb/Core/ValueObjectConstResultImpl.h new file mode 100644 index 00000000000..271b9382356 --- /dev/null +++ b/include/lldb/Core/ValueObjectConstResultImpl.h @@ -0,0 +1,96 @@ +//===-- ValueObjectConstResultImpl.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectConstResultImpl_h_ +#define liblldb_ValueObjectConstResultImpl_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A class wrapping common implementation details for operations in +// ValueObjectConstResult ( & Child ) that may need to jump from the host +// memory space into the target's memory space +//---------------------------------------------------------------------- +class ValueObjectConstResultImpl +{ +public: + + ValueObjectConstResultImpl (ValueObject* valobj, + lldb::addr_t live_address = LLDB_INVALID_ADDRESS); + + virtual + ~ValueObjectConstResultImpl() + { + } + + lldb::ValueObjectSP + Dereference (Error &error); + + ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + lldb::ValueObjectSP + GetSyntheticChildAtOffset (uint32_t offset, const ClangASTType& type, bool can_create); + + lldb::ValueObjectSP + AddressOf (Error &error); + + bool + NeedsDerefOnTarget() + { + m_impl_backend->UpdateValueIfNeeded(false); + return (m_impl_backend->GetValue().GetValueType() == Value::eValueTypeHostAddress); + } + + lldb::addr_t + GetLiveAddress() + { + return m_live_address; + } + + void + SetLiveAddress(lldb::addr_t addr = LLDB_INVALID_ADDRESS, + AddressType address_type = eAddressTypeLoad) + { + m_live_address = addr; + m_live_address_type = address_type; + } + + lldb::ValueObjectSP + DerefOnTarget(); + + virtual lldb::addr_t + GetAddressOf (bool scalar_is_load_address = true, + AddressType *address_type = NULL); + + virtual size_t + GetPointeeData (DataExtractor& data, + uint32_t item_idx = 0, + uint32_t item_count = 1); + +private: + + ValueObject *m_impl_backend; + lldb::addr_t m_live_address; + AddressType m_live_address_type; + lldb::ValueObjectSP m_load_addr_backend; + lldb::ValueObjectSP m_address_of_backend; + + DISALLOW_COPY_AND_ASSIGN (ValueObjectConstResultImpl); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectConstResultImpl_h_ diff --git a/include/lldb/Core/ValueObjectDynamicValue.h b/include/lldb/Core/ValueObjectDynamicValue.h new file mode 100644 index 00000000000..c0f6baade3f --- /dev/null +++ b/include/lldb/Core/ValueObjectDynamicValue.h @@ -0,0 +1,133 @@ +//===-- ValueObjectDynamicValue.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectDynamicValue_h_ +#define liblldb_ValueObjectDynamicValue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that represents memory at a given address, viewed as some +// set lldb type. +//---------------------------------------------------------------------- +class ValueObjectDynamicValue : public ValueObject +{ +public: + virtual + ~ValueObjectDynamicValue(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual bool + IsDynamic () + { + return true; + } + + virtual ValueObject * + GetParent() + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual const ValueObject * + GetParent() const + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual lldb::ValueObjectSP + GetStaticValue () + { + return m_parent->GetSP(); + } + + void + SetOwningSP (lldb::ValueObjectSP &owning_sp) + { + if (m_owning_valobj_sp == owning_sp) + return; + + assert (m_owning_valobj_sp.get() == NULL); + m_owning_valobj_sp = owning_sp; + } + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + virtual bool + SetData (DataExtractor &data, Error &error); + +protected: + virtual bool + UpdateValue (); + + virtual lldb::DynamicValueType + GetDynamicValueTypeImpl () + { + return m_use_dynamic; + } + + virtual bool + HasDynamicValueTypeInfo () + { + return true; + } + + virtual ClangASTType + GetClangTypeImpl (); + + Address m_address; ///< The variable that this value object is based upon + TypeAndOrName m_dynamic_type_info; // We can have a type_sp or just a name + lldb::ValueObjectSP m_owning_valobj_sp; + lldb::DynamicValueType m_use_dynamic; + +private: + friend class ValueObject; + friend class ValueObjectConstResult; + ValueObjectDynamicValue (ValueObject &parent, lldb::DynamicValueType use_dynamic); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectDynamicValue); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectDynamicValue_h_ diff --git a/include/lldb/Core/ValueObjectList.h b/include/lldb/Core/ValueObjectList.h new file mode 100644 index 00000000000..5bfe40b2e95 --- /dev/null +++ b/include/lldb/Core/ValueObjectList.h @@ -0,0 +1,90 @@ +//===-- ValueObjectList.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectList_h_ +#define liblldb_ValueObjectList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/UserID.h" +#include "lldb/Target/ExecutionContextScope.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A collection of ValueObject values that +//---------------------------------------------------------------------- +class ValueObjectList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ValueObjectList (); + + ValueObjectList (const ValueObjectList &rhs); + + ~ValueObjectList(); + + const ValueObjectList & + operator = (const ValueObjectList &rhs); + + void + Append (const lldb::ValueObjectSP &val_obj_sp); + + void + Append (const ValueObjectList &valobj_list); + + lldb::ValueObjectSP + FindValueObjectByPointer (ValueObject *valobj); + + size_t + GetSize () const; + + void + Resize (size_t size); + + lldb::ValueObjectSP + GetValueObjectAtIndex (size_t idx); + + lldb::ValueObjectSP + RemoveValueObjectAtIndex (size_t idx); + + void + SetValueObjectAtIndex (size_t idx, + const lldb::ValueObjectSP &valobj_sp); + + lldb::ValueObjectSP + FindValueObjectByValueName (const char *name); + + lldb::ValueObjectSP + FindValueObjectByUID (lldb::user_id_t uid); + + void + Swap (ValueObjectList &value_object_list); + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Classes that inherit from ValueObjectList can see and modify these + //------------------------------------------------------------------ + collection m_value_objects; + +}; + + +} // namespace lldb_private + +#endif // liblldb_ValueObjectList_h_ diff --git a/include/lldb/Core/ValueObjectMemory.h b/include/lldb/Core/ValueObjectMemory.h new file mode 100644 index 00000000000..627d73eb4b2 --- /dev/null +++ b/include/lldb/Core/ValueObjectMemory.h @@ -0,0 +1,91 @@ +//===-- ValueObjectMemory.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectMemory_h_ +#define liblldb_ValueObjectMemory_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that represents memory at a given address, viewed as some +// set lldb type. +//---------------------------------------------------------------------- +class ValueObjectMemory : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp); + + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type); + + virtual + ~ValueObjectMemory(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual lldb::ModuleSP + GetModule(); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + Address m_address; ///< The variable that this value object is based upon + lldb::TypeSP m_type_sp; + ClangASTType m_clang_type; + +private: + ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp); + + ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type); + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectMemory); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectMemory_h_ diff --git a/include/lldb/Core/ValueObjectRegister.h b/include/lldb/Core/ValueObjectRegister.h new file mode 100644 index 00000000000..6820629f08e --- /dev/null +++ b/include/lldb/Core/ValueObjectRegister.h @@ -0,0 +1,195 @@ +//===-- ValueObjectRegister.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectRegister_h_ +#define liblldb_ValueObjectRegister_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that contains a root variable that may or may not +// have children. +//---------------------------------------------------------------------- +class ValueObjectRegisterContext : public ValueObject +{ +public: + + virtual + ~ValueObjectRegisterContext(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegisterSet; + } + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::RegisterContextSP m_reg_ctx_sp; + +private: + ValueObjectRegisterContext (ValueObject &parent, lldb::RegisterContextSP ®_ctx_sp); + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegisterContext); +}; + +class ValueObjectRegisterSet : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t set_idx); + + virtual + ~ValueObjectRegisterSet(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegisterSet; + } + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual ValueObject * + CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index); + + virtual lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::RegisterContextSP m_reg_ctx_sp; + const RegisterSet *m_reg_set; + uint32_t m_reg_set_idx; + +private: + friend class ValueObjectRegisterContext; + ValueObjectRegisterSet (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t set_idx); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegisterSet); +}; + +class ValueObjectRegister : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num); + + virtual + ~ValueObjectRegister(); + + virtual uint64_t + GetByteSize(); + + virtual lldb::ValueType + GetValueType () const + { + return lldb::eValueTypeRegister; + } + + virtual ConstString + GetTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + virtual bool + SetData (DataExtractor &data, Error &error); + + virtual bool + ResolveValue (Scalar &scalar); + + virtual void + GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat = eGetExpressionPathFormatDereferencePointers); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::RegisterContextSP m_reg_ctx_sp; + RegisterInfo m_reg_info; + RegisterValue m_reg_value; + ConstString m_type_name; + ClangASTType m_clang_type; + +private: + void + ConstructObject (uint32_t reg_num); + + friend class ValueObjectRegisterSet; + ValueObjectRegister (ValueObject &parent, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num); + ValueObjectRegister (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectRegister); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectRegister_h_ diff --git a/include/lldb/Core/ValueObjectSyntheticFilter.h b/include/lldb/Core/ValueObjectSyntheticFilter.h new file mode 100644 index 00000000000..f1d8c885c25 --- /dev/null +++ b/include/lldb/Core/ValueObjectSyntheticFilter.h @@ -0,0 +1,182 @@ +//===-- ValueObjectSyntheticFilter.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectSyntheticFilter_h_ +#define liblldb_ValueObjectSyntheticFilter_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that obtains its children from some source other than +// real information +// This is currently used to implement Python-based children and filters +// but you can bind it to any source of synthetic information and have +// it behave accordingly +//---------------------------------------------------------------------- +class ValueObjectSynthetic : public ValueObject +{ +public: + virtual + ~ValueObjectSynthetic(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual bool + MightHaveChildren(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx, bool can_create); + + virtual lldb::ValueObjectSP + GetChildMemberWithName (const ConstString &name, bool can_create); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual lldb::ValueObjectSP + GetDynamicValue (lldb::DynamicValueType valueType); + + virtual bool + IsInScope (); + + virtual bool + HasSyntheticValue() + { + return false; + } + + virtual bool + IsSynthetic() { return true; } + + virtual void + CalculateSyntheticValue (bool use_synthetic) + { + } + + virtual bool + IsDynamic () + { + if (m_parent) + return m_parent->IsDynamic(); + else + return false; + } + + virtual lldb::ValueObjectSP + GetStaticValue () + { + if (m_parent) + return m_parent->GetStaticValue(); + else + return GetSP(); + } + + virtual lldb::DynamicValueType + GetDynamicValueType () + { + if (m_parent) + return m_parent->GetDynamicValueType(); + else + return lldb::eNoDynamicValues; + } + + virtual ValueObject * + GetParent() + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual const ValueObject * + GetParent() const + { + if (m_parent) + return m_parent->GetParent(); + else + return NULL; + } + + virtual lldb::ValueObjectSP + GetNonSyntheticValue (); + + virtual bool + ResolveValue (Scalar &scalar) + { + if (m_parent) + return m_parent->ResolveValue(scalar); + return false; + } + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + virtual void + CreateSynthFilter (); + + // we need to hold on to the SyntheticChildren because someone might delete the type binding while we are alive + lldb::SyntheticChildrenSP m_synth_sp; + std::unique_ptr m_synth_filter_ap; + + typedef std::map ByIndexMap; + typedef std::map NameToIndexMap; + + typedef ByIndexMap::iterator ByIndexIterator; + typedef NameToIndexMap::iterator NameToIndexIterator; + + ByIndexMap m_children_byindex; + NameToIndexMap m_name_toindex; + uint32_t m_synthetic_children_count; // FIXME use the ValueObject's ChildrenManager instead of a special purpose solution + + ConstString m_parent_type_name; + + LazyBool m_might_have_children; + +private: + friend class ValueObject; + ValueObjectSynthetic (ValueObject &parent, lldb::SyntheticChildrenSP filter); + + void + CopyParentData (); + + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectSynthetic); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectSyntheticFilter_h_ diff --git a/include/lldb/Core/ValueObjectVariable.h b/include/lldb/Core/ValueObjectVariable.h new file mode 100644 index 00000000000..8a30b00f6bb --- /dev/null +++ b/include/lldb/Core/ValueObjectVariable.h @@ -0,0 +1,90 @@ +//===-- ValueObjectVariable.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ValueObjectVariable_h_ +#define liblldb_ValueObjectVariable_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A ValueObject that contains a root variable that may or may not +// have children. +//---------------------------------------------------------------------- +class ValueObjectVariable : public ValueObject +{ +public: + static lldb::ValueObjectSP + Create (ExecutionContextScope *exe_scope, const lldb::VariableSP &var_sp); + + virtual + ~ValueObjectVariable(); + + virtual uint64_t + GetByteSize(); + + virtual ConstString + GetTypeName(); + + virtual ConstString + GetQualifiedTypeName(); + + virtual size_t + CalculateNumChildren(); + + virtual lldb::ValueType + GetValueType() const; + + virtual bool + IsInScope (); + + virtual lldb::ModuleSP + GetModule(); + + virtual SymbolContextScope * + GetSymbolContextScope(); + + virtual bool + GetDeclaration (Declaration &decl); + + virtual const char * + GetLocationAsCString (); + + virtual bool + SetValueFromCString (const char *value_str, Error& error); + + virtual bool + SetData (DataExtractor &data, Error &error); + +protected: + virtual bool + UpdateValue (); + + virtual ClangASTType + GetClangTypeImpl (); + + lldb::VariableSP m_variable_sp; ///< The variable that this value object is based upon + Value m_resolved_value; ///< The value that DWARFExpression resolves this variable to before we patch it up + +private: + ValueObjectVariable (ExecutionContextScope *exe_scope, const lldb::VariableSP &var_sp); + //------------------------------------------------------------------ + // For ValueObject only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (ValueObjectVariable); +}; + +} // namespace lldb_private + +#endif // liblldb_ValueObjectVariable_h_ diff --git a/include/lldb/Core/dwarf.h b/include/lldb/Core/dwarf.h new file mode 100644 index 00000000000..bf77125d86a --- /dev/null +++ b/include/lldb/Core/dwarf.h @@ -0,0 +1,64 @@ +//===-- dwarf.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef DebugBase_dwarf_h_ +#define DebugBase_dwarf_h_ + +#include +#include + +// Get the DWARF constant defintions from llvm +#include "llvm/Support/Dwarf.h" +// and stuff them in our default namespace +using namespace llvm::dwarf; + +typedef uint32_t dw_uleb128_t; +typedef int32_t dw_sleb128_t; +typedef uint16_t dw_attr_t; +typedef uint8_t dw_form_t; +typedef uint16_t dw_tag_t; +typedef uint64_t dw_addr_t; // Dwarf address define that must be big enough for any addresses in the compile units that get parsed + +#ifdef DWARFUTILS_DWARF64 +#define DWARF_REF_ADDR_SIZE 8 +typedef uint64_t dw_offset_t; // Dwarf Debug Information Entry offset for any offset into the file +#else +#define DWARF_REF_ADDR_SIZE 4 +typedef uint32_t dw_offset_t; // Dwarf Debug Information Entry offset for any offset into the file +#endif + +/* Constants */ +#define DW_INVALID_OFFSET (~(dw_offset_t)0) +#define DW_INVALID_INDEX 0xFFFFFFFFul + +// #define DW_ADDR_none 0x0 + +#define DW_EH_PE_MASK_ENCODING 0x0F + +//// The following are used only internally within lldb - don't +//// document them in the llvm Dwarf.h header file, we won't see +//// them in executable files anywhere. +//// These constants fit between DW_OP_lo_user (0xe0) and DW_OP_hi_user (0xff). +// +//#define DW_OP_APPLE_array_ref 0xEE // first pops index, then pops array; pushes array[index] +//#define DW_OP_APPLE_extern 0xEF // ULEB128 index of external object (i.e., an entity from the program that was used in the expression) +#define DW_OP_APPLE_uninit 0xF0 // This is actually generated by some apple compilers in locations lists +//#define DW_OP_APPLE_assign 0xF1 // pops value off and assigns it to second item on stack (2nd item must have assignable context) +//#define DW_OP_APPLE_address_of 0xF2 // gets the address of the top stack item (top item must be a variable, or have value_type that is an address already) +//#define DW_OP_APPLE_value_of 0xF3 // pops the value off the stack and pushes the value of that object (top item must be a variable, or expression local) +//#define DW_OP_APPLE_deref_type 0xF4 // gets the address of the top stack item (top item must be a variable, or a clang type) +//#define DW_OP_APPLE_expr_local 0xF5 // ULEB128 expression local index +//#define DW_OP_APPLE_constf 0xF6 // 1 byte float size, followed by constant float data +//#define DW_OP_APPLE_scalar_cast 0xF7 // Cast top of stack to 2nd in stack's type leaving all items in place +//#define DW_OP_APPLE_clang_cast 0xF8 // pointer size clang::Type * off the stack and cast top stack item to this type +//#define DW_OP_APPLE_clear 0xFE // clears the entire expression stack, ok if the stack is empty +//#define DW_OP_APPLE_error 0xFF // Stops expression evaluation and returns an error (no args) + + +#endif // DebugBase_dwarf_h_ diff --git a/include/lldb/DataFormatters/CXXFormatterFunctions.h b/include/lldb/DataFormatters/CXXFormatterFunctions.h new file mode 100644 index 00000000000..2f56c56810a --- /dev/null +++ b/include/lldb/DataFormatters/CXXFormatterFunctions.h @@ -0,0 +1,874 @@ +//===-- CXXFormatterFunctions.h------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CXXFormatterFunctions_h_ +#define liblldb_CXXFormatterFunctions_h_ + +#include +#include + +#include "lldb/lldb-forward.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/ASTContext.h" + +namespace lldb_private { + namespace formatters + { + bool + ExtractValueFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + uint64_t &value); + + bool + ExtractSummaryFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + Stream &stream); + + lldb::ValueObjectSP + CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + uint64_t index); + + lldb::ValueObjectSP + CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + const char* key); + + size_t + ExtractIndexFromString (const char* item_name); + + time_t + GetOSXEpoch (); + + bool + Char16StringSummaryProvider (ValueObject& valobj, Stream& stream); // char16_t* and unichar* + + bool + Char32StringSummaryProvider (ValueObject& valobj, Stream& stream); // char32_t* + + bool + WCharStringSummaryProvider (ValueObject& valobj, Stream& stream); // wchar_t* + + bool + Char16SummaryProvider (ValueObject& valobj, Stream& stream); // char16_t and unichar + + bool + Char32SummaryProvider (ValueObject& valobj, Stream& stream); // char32_t + + bool + WCharSummaryProvider (ValueObject& valobj, Stream& stream); // wchar_t + + bool + LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::string + + bool + LibcxxWStringSummaryProvider (ValueObject& valobj, Stream& stream); // libc++ std::wstring + + bool + ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream); + + SyntheticChildrenFrontEnd* ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + template + bool + NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSArraySummaryProvider (ValueObject& valobj, Stream& stream); + + template + bool + NSSetSummaryProvider (ValueObject& valobj, Stream& stream); + + template + bool + NSDataSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSNumberSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFBagSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFBinaryHeapSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFBitVectorSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSDateSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + CFAbsoluteTimeSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSBundleSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSStringSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + NSURLSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream); + + template + bool + ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream); + + bool + RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream); + + extern template bool + NSDictionarySummaryProvider (ValueObject&, Stream&) ; + + extern template bool + NSDictionarySummaryProvider (ValueObject&, Stream&) ; + + extern template bool + NSDataSummaryProvider (ValueObject&, Stream&) ; + + extern template bool + NSDataSummaryProvider (ValueObject&, Stream&) ; + + extern template bool + ObjCSELSummaryProvider (ValueObject&, Stream&); + + extern template bool + ObjCSELSummaryProvider (ValueObject&, Stream&); + + class NSArrayMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + 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; + }; + public: + NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + ClangASTType m_id_type; + std::vector m_children; + }; + + class NSArrayISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayISyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + uint64_t m_items; + lldb::addr_t m_data_ptr; + ClangASTType m_id_type; + std::vector m_children; + }; + + class NSArrayCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSArrayCodeRunningSyntheticFrontEnd (); + }; + + SyntheticChildrenFrontEnd* NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class NSDictionaryISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + 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; + }; + + public: + NSDictionaryISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryISyntheticFrontEnd (); + private: + 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; + ClangASTType m_pair_type; + std::vector m_children; + }; + + class NSDictionaryMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + 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; + }; + public: + NSDictionaryMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + lldb::ByteOrder m_order; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + ClangASTType m_pair_type; + std::vector m_children; + }; + + class NSDictionaryCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSDictionaryCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSDictionaryCodeRunningSyntheticFrontEnd (); + }; + + SyntheticChildrenFrontEnd* NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class NSSetISyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + 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; + }; + + public: + NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetISyntheticFrontEnd (); + private: + 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 m_children; + }; + + class NSOrderedSetSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + + public: + NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSOrderedSetSyntheticFrontEnd (); + private: + uint32_t m_count; + std::map m_children; + }; + + class NSSetMSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + private: + 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; + }; + struct SetItemDescriptor + { + lldb::addr_t item_ptr; + lldb::ValueObjectSP valobj_sp; + }; + public: + NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetMSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint8_t m_ptr_size; + DataDescriptor_32 *m_data_32; + DataDescriptor_64 *m_data_64; + std::vector m_children; + }; + + class NSSetCodeRunningSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + NSSetCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~NSSetCodeRunningSyntheticFrontEnd (); + }; + + SyntheticChildrenFrontEnd* NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxVectorBoolSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count; + lldb::addr_t m_base_data_address; + EvaluateExpressionOptions m_options; + }; + + SyntheticChildrenFrontEnd* LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + bool + LibcxxContainerSummaryProvider (ValueObject& valobj, Stream& stream); + + class LibstdcppVectorBoolSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibstdcppVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibstdcppVectorBoolSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + uint64_t m_count; + lldb::addr_t m_base_data_address; + EvaluateExpressionOptions m_options; + }; + + SyntheticChildrenFrontEnd* LibstdcppVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibstdcppMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibstdcppMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibstdcppMapIteratorSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + lldb::addr_t m_pair_address; + ClangASTType m_pair_type; + EvaluateExpressionOptions m_options; + lldb::ValueObjectSP m_pair_sp; + }; + + SyntheticChildrenFrontEnd* LibstdcppMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibCxxMapIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibCxxMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibCxxMapIteratorSyntheticFrontEnd (); + private: + ValueObject *m_pair_ptr; + }; + + SyntheticChildrenFrontEnd* LibCxxMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class VectorIteratorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + VectorIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp, + ConstString item_name); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~VectorIteratorSyntheticFrontEnd (); + private: + ExecutionContextRef m_exe_ctx_ref; + ConstString m_item_name; + lldb::ValueObjectSP m_item_sp; + }; + + SyntheticChildrenFrontEnd* LibCxxVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + SyntheticChildrenFrontEnd* LibStdcppVectorIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxSharedPtrSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxSharedPtrSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxSharedPtrSyntheticFrontEnd (); + 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* LibcxxSharedPtrSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxStdVectorSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdVectorSyntheticFrontEnd (); + private: + ValueObject* m_start; + ValueObject* m_finish; + ClangASTType m_element_type; + uint32_t m_element_size; + std::map m_children; + }; + + SyntheticChildrenFrontEnd* LibcxxStdVectorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxStdListSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdListSyntheticFrontEnd (); + private: + bool + HasLoop(); + + size_t m_list_capping_size; + static const bool g_use_loop_detect = true; + lldb::addr_t m_node_address; + ValueObject* m_head; + ValueObject* m_tail; + ClangASTType m_element_type; + size_t m_count; + std::map m_children; + }; + + SyntheticChildrenFrontEnd* LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + class LibcxxStdMapSyntheticFrontEnd : public SyntheticChildrenFrontEnd + { + public: + LibcxxStdMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp); + + virtual size_t + CalculateNumChildren (); + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update(); + + virtual bool + MightHaveChildren (); + + virtual size_t + GetIndexOfChildWithName (const ConstString &name); + + virtual + ~LibcxxStdMapSyntheticFrontEnd (); + private: + bool + GetDataType(); + + void + GetValueOffset (const lldb::ValueObjectSP& node); + + ValueObject* m_tree; + ValueObject* m_root_node; + ClangASTType m_element_type; + uint32_t m_skip_size; + size_t m_count; + std::map m_children; + }; + + SyntheticChildrenFrontEnd* LibcxxStdMapSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP); + + } // namespace formatters +} // namespace lldb_private + +#endif // liblldb_CXXFormatterFunctions_h_ diff --git a/include/lldb/DataFormatters/DataVisualization.h b/include/lldb/DataFormatters/DataVisualization.h new file mode 100644 index 00000000000..499e0fe14d9 --- /dev/null +++ b/include/lldb/DataFormatters/DataVisualization.h @@ -0,0 +1,174 @@ +//===-- DataVisualization.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_DataVisualization_h_ +#define lldb_DataVisualization_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/DataFormatters/FormatManager.h" + +namespace lldb_private { + +// this class is the high-level front-end of LLDB Data Visualization +// code in FormatManager.h/cpp is the low-level implementation of this feature +// clients should refer to this class as the entry-point into the data formatters +// unless they have a good reason to bypass this and go to the backend +class DataVisualization +{ +public: + + // use this call to force the FM to consider itself updated even when there is no apparent reason for that + static void + ForceUpdate(); + + static uint32_t + GetCurrentRevision (); + + class ValueFormats + { + public: + static lldb::TypeFormatImplSP + GetFormat (ValueObject& valobj, lldb::DynamicValueType use_dynamic); + + static lldb::TypeFormatImplSP + GetFormat (const ConstString &type); + + static void + Add (const ConstString &type, const lldb::TypeFormatImplSP &entry); + + static bool + Delete (const ConstString &type); + + static void + Clear (); + + static void + LoopThrough (TypeFormatImpl::ValueCallback callback, void* callback_baton); + + static size_t + GetCount (); + + static lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForFormatAtIndex (size_t); + + static lldb::TypeFormatImplSP + GetFormatAtIndex (size_t); + }; + + static lldb::TypeSummaryImplSP + GetSummaryFormat(ValueObject& valobj, + lldb::DynamicValueType use_dynamic); + + static lldb::TypeSummaryImplSP + GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + static lldb::SyntheticChildrenSP + GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + + static lldb::TypeFilterImplSP + GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + static lldb::ScriptedSyntheticChildrenSP + GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + +#ifndef LLDB_DISABLE_PYTHON + static lldb::SyntheticChildrenSP + GetSyntheticChildren(ValueObject& valobj, + lldb::DynamicValueType use_dynamic); +#endif + + static bool + AnyMatches(ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items = TypeCategoryImpl::ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + TypeCategoryImpl::FormatCategoryItems* matching_type = NULL); + + class NamedSummaryFormats + { + public: + static bool + GetSummaryFormat (const ConstString &type, lldb::TypeSummaryImplSP &entry); + + static void + Add (const ConstString &type, const lldb::TypeSummaryImplSP &entry); + + static bool + Delete (const ConstString &type); + + static void + Clear (); + + static void + LoopThrough (TypeSummaryImpl::SummaryCallback callback, void* callback_baton); + + static uint32_t + GetCount (); + }; + + class Categories + { + public: + + static bool + GetCategory (const ConstString &category, + lldb::TypeCategoryImplSP &entry, + bool allow_create = true); + + static void + Add (const ConstString &category); + + static bool + Delete (const ConstString &category); + + static void + Clear (); + + static void + Clear (const ConstString &category); + + static void + Enable (const ConstString& category, + TypeCategoryMap::Position = TypeCategoryMap::Default); + + static void + Disable (const ConstString& category); + + static void + Enable (const lldb::TypeCategoryImplSP& category, + TypeCategoryMap::Position = TypeCategoryMap::Default); + + static void + Disable (const lldb::TypeCategoryImplSP& category); + + static void + LoopThrough (FormatManager::CategoryCallback callback, void* callback_baton); + + static uint32_t + GetCount (); + + static lldb::TypeCategoryImplSP + GetCategoryAtIndex (size_t); + }; +}; + + +} // namespace lldb_private + +#endif // lldb_DataVisualization_h_ diff --git a/include/lldb/DataFormatters/FormatCache.h b/include/lldb/DataFormatters/FormatCache.h new file mode 100644 index 00000000000..941b96c1fac --- /dev/null +++ b/include/lldb/DataFormatters/FormatCache.h @@ -0,0 +1,101 @@ +//===-- FormatCache.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatCache_h_ +#define lldb_FormatCache_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/ConstString.h" +#include "lldb/DataFormatters/FormatClasses.h" + +namespace lldb_private { +class FormatCache +{ +private: + struct Entry + { + private: + bool m_summary_cached : 1; + bool m_synthetic_cached : 1; + + lldb::TypeSummaryImplSP m_summary_sp; + lldb::SyntheticChildrenSP m_synthetic_sp; + public: + Entry (); + Entry (lldb::TypeSummaryImplSP); + Entry (lldb::SyntheticChildrenSP); + Entry (lldb::TypeSummaryImplSP,lldb::SyntheticChildrenSP); + + bool + IsSummaryCached (); + + bool + IsSyntheticCached (); + + lldb::TypeSummaryImplSP + GetSummary (); + + lldb::SyntheticChildrenSP + GetSynthetic (); + + void + SetSummary (lldb::TypeSummaryImplSP); + + void + SetSynthetic (lldb::SyntheticChildrenSP); + }; + typedef std::map CacheMap; + CacheMap m_map; + Mutex m_mutex; + + uint64_t m_cache_hits; + uint64_t m_cache_misses; + + Entry& + GetEntry (const ConstString& type); + +public: + FormatCache (); + + bool + GetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp); + + bool + GetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp); + + void + SetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp); + + void + SetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp); + + void + Clear (); + + uint64_t + GetCacheHits () + { + return m_cache_hits; + } + + uint64_t + GetCacheMisses () + { + return m_cache_misses; + } +}; +} // namespace lldb_private + +#endif // lldb_FormatCache_h_ diff --git a/include/lldb/DataFormatters/FormatClasses.h b/include/lldb/DataFormatters/FormatClasses.h new file mode 100644 index 00000000000..48a8eda4ad4 --- /dev/null +++ b/include/lldb/DataFormatters/FormatClasses.h @@ -0,0 +1,128 @@ +//===-- FormatClasses.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatClasses_h_ +#define lldb_FormatClasses_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/DataFormatters/TypeFormat.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/DataFormatters/TypeSynthetic.h" + +namespace lldb_private { + +class TypeNameSpecifierImpl +{ +public: + TypeNameSpecifierImpl() : + m_is_regex(false), + m_type() + { + } + + TypeNameSpecifierImpl (const char* name, bool is_regex) : + m_is_regex(is_regex), + m_type() + { + if (name) + m_type.m_type_name.assign(name); + } + + // if constructing with a given type, is_regex cannot be true since we are + // giving an exact type to match + TypeNameSpecifierImpl (lldb::TypeSP type) : + m_is_regex(false), + m_type() + { + if (type) + { + m_type.m_type_name.assign(type->GetName().GetCString()); + m_type.m_typeimpl_sp = lldb::TypeImplSP(new TypeImpl(type)); + } + } + + TypeNameSpecifierImpl (ClangASTType type) : + m_is_regex(false), + m_type() + { + if (type.IsValid()) + { + m_type.m_type_name.assign(type.GetConstTypeName().GetCString()); + m_type.m_typeimpl_sp = lldb::TypeImplSP(new TypeImpl(type)); + } + } + + const char* + GetName() + { + if (m_type.m_type_name.size()) + return m_type.m_type_name.c_str(); + return NULL; + } + + lldb::TypeSP + GetTypeSP () + { + if (m_type.m_typeimpl_sp && m_type.m_typeimpl_sp->IsValid()) + return m_type.m_typeimpl_sp->GetTypeSP(); + return lldb::TypeSP(); + } + + ClangASTType + GetClangASTType () + { + if (m_type.m_typeimpl_sp && m_type.m_typeimpl_sp->IsValid()) + return m_type.m_typeimpl_sp->GetClangASTType(); + return ClangASTType(); + } + + bool + IsRegex() + { + return m_is_regex; + } + +private: + bool m_is_regex; + // this works better than TypeAndOrName because the latter only wraps a TypeSP + // whereas TypeImplSP can also be backed by a ClangASTType which is more commonly + // used in LLDB. moreover, TypeImplSP is also what is currently backing SBType + struct TypeOrName + { + std::string m_type_name; + lldb::TypeImplSP m_typeimpl_sp; + }; + TypeOrName m_type; + + +private: + DISALLOW_COPY_AND_ASSIGN(TypeNameSpecifierImpl); +}; + +} // namespace lldb_private + +#endif // lldb_FormatClasses_h_ diff --git a/include/lldb/DataFormatters/FormatManager.h b/include/lldb/DataFormatters/FormatManager.h new file mode 100644 index 00000000000..162e25143f2 --- /dev/null +++ b/include/lldb/DataFormatters/FormatManager.h @@ -0,0 +1,251 @@ +//===-- FormatManager.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatManager_h_ +#define lldb_FormatManager_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/DataFormatters/FormatCache.h" +#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/TypeCategory.h" +#include "lldb/DataFormatters/TypeCategoryMap.h" + +namespace lldb_private { + +// this file (and its. cpp) contain the low-level implementation of LLDB Data Visualization +// class DataVisualization is the high-level front-end of this feature +// clients should refer to that class as the entry-point into the data formatters +// unless they have a good reason to bypass it and prefer to use this file's objects directly + +class FormatManager : public IFormatChangeListener +{ + typedef FormatNavigator ValueNavigator; + typedef ValueNavigator::MapType ValueMap; + typedef FormatMap NamedSummariesMap; + typedef TypeCategoryMap::MapType::iterator CategoryMapIterator; +public: + + typedef TypeCategoryMap::CallbackType CategoryCallback; + + FormatManager (); + + ValueNavigator& + GetValueNavigator () + { + return m_value_nav; + } + + NamedSummariesMap& + GetNamedSummaryNavigator () + { + return m_named_summaries_map; + } + + void + EnableCategory (const ConstString& category_name, + TypeCategoryMap::Position pos = TypeCategoryMap::Default) + { + m_categories_map.Enable(category_name, + pos); + } + + void + DisableCategory (const ConstString& category_name) + { + m_categories_map.Disable(category_name); + } + + void + EnableCategory (const lldb::TypeCategoryImplSP& category, + TypeCategoryMap::Position pos = TypeCategoryMap::Default) + { + m_categories_map.Enable(category, + pos); + } + + void + DisableCategory (const lldb::TypeCategoryImplSP& category) + { + m_categories_map.Disable(category); + } + + bool + DeleteCategory (const ConstString& category_name) + { + return m_categories_map.Delete(category_name); + } + + void + ClearCategories () + { + return m_categories_map.Clear(); + } + + uint32_t + GetCategoriesCount () + { + return m_categories_map.GetCount(); + } + + lldb::TypeCategoryImplSP + GetCategoryAtIndex (size_t index) + { + return m_categories_map.GetAtIndex(index); + } + + void + LoopThroughCategories (CategoryCallback callback, void* param) + { + m_categories_map.LoopThrough(callback, param); + } + + lldb::TypeCategoryImplSP + GetCategory (const char* category_name = NULL, + bool can_create = true) + { + if (!category_name) + return GetCategory(m_default_category_name); + return GetCategory(ConstString(category_name)); + } + + lldb::TypeCategoryImplSP + GetCategory (const ConstString& category_name, + bool can_create = true); + + lldb::TypeSummaryImplSP + GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); + + lldb::TypeFilterImplSP + GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + lldb::ScriptedSyntheticChildrenSP + GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + +#ifndef LLDB_DISABLE_PYTHON + lldb::SyntheticChildrenSP + GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + + lldb::TypeSummaryImplSP + GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SyntheticChildrenSP + GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); +#endif + + bool + AnyMatches (ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items = TypeCategoryImpl::ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + TypeCategoryImpl::FormatCategoryItems* matching_type = NULL) + { + return m_categories_map.AnyMatches(type_name, + items, + only_enabled, + matching_category, + matching_type); + } + + static bool + GetFormatFromCString (const char *format_cstr, + bool partial_match_ok, + lldb::Format &format); + + static char + GetFormatAsFormatChar (lldb::Format format); + + static const char * + GetFormatAsCString (lldb::Format format); + + // if the user tries to add formatters for, say, "struct Foo" + // those will not match any type because of the way we strip qualifiers from typenames + // this method looks for the case where the user is adding a "class","struct","enum" or "union" Foo + // and strips the unnecessary qualifier + static ConstString + GetValidTypeName (const ConstString& type); + + // when DataExtractor dumps a vectorOfT, it uses a predefined format for each item + // this method returns it, or eFormatInvalid if vector_format is not a vectorOf + static lldb::Format + GetSingleItemFormat (lldb::Format vector_format); + + void + Changed () + { + __sync_add_and_fetch(&m_last_revision, +1); + m_format_cache.Clear (); + } + + uint32_t + GetCurrentRevision () + { + return m_last_revision; + } + + ~FormatManager () + { + } + +private: + FormatCache m_format_cache; + ValueNavigator m_value_nav; + NamedSummariesMap m_named_summaries_map; + uint32_t m_last_revision; + TypeCategoryMap m_categories_map; + + ConstString m_default_category_name; + ConstString m_system_category_name; + ConstString m_gnu_cpp_category_name; + ConstString m_libcxx_category_name; + ConstString m_objc_category_name; + ConstString m_corefoundation_category_name; + ConstString m_coregraphics_category_name; + ConstString m_coreservices_category_name; + ConstString m_vectortypes_category_name; + ConstString m_appkit_category_name; + + TypeCategoryMap& + GetCategories () + { + return m_categories_map; + } + + // WARNING: these are temporary functions that setup formatters + // while a few of these actually should be globally available and setup by LLDB itself + // most would actually belong to the users' lldbinit file or to some other form of configurable + // storage + void + LoadLibStdcppFormatters (); + + void + LoadLibcxxFormatters (); + + void + LoadSystemFormatters (); + + void + LoadObjCFormatters (); +}; + +} // namespace lldb_private + +#endif // lldb_FormatManager_h_ diff --git a/include/lldb/DataFormatters/FormatNavigator.h b/include/lldb/DataFormatters/FormatNavigator.h new file mode 100644 index 00000000000..a738cfd069e --- /dev/null +++ b/include/lldb/DataFormatters/FormatNavigator.h @@ -0,0 +1,690 @@ +//===-- FormatNavigator.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_FormatNavigator_h_ +#define lldb_FormatNavigator_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" +#include "clang/AST/DeclObjC.h" + +// Project includes +#include "lldb/lldb-public.h" + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/DataFormatters/FormatClasses.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" + +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/TargetList.h" + +namespace lldb_private { + +// this file (and its. cpp) contain the low-level implementation of LLDB Data Visualization +// class DataVisualization is the high-level front-end of this feature +// clients should refer to that class as the entry-point into the data formatters +// unless they have a good reason to bypass it and prefer to use this file's objects directly +class IFormatChangeListener +{ +public: + virtual void + Changed () = 0; + + virtual + ~IFormatChangeListener () {} + + virtual uint32_t + GetCurrentRevision () = 0; + +}; + +static inline bool +IsWhitespace (char c) +{ + return ( (c == ' ') || (c == '\t') || (c == '\v') || (c == '\f') ); +} + +static inline bool +HasPrefix (const char* str1, const char* str2) +{ + return ( ::strstr(str1, str2) == str1 ); +} + +// if the user tries to add formatters for, say, "struct Foo" +// those will not match any type because of the way we strip qualifiers from typenames +// this method looks for the case where the user is adding a "class","struct","enum" or "union" Foo +// and strips the unnecessary qualifier +static inline ConstString +GetValidTypeName_Impl (const ConstString& type) +{ + int strip_len = 0; + + if (type == false) + return type; + + const char* type_cstr = type.AsCString(); + + if ( HasPrefix(type_cstr, "class ") ) + strip_len = 6; + else if ( HasPrefix(type_cstr, "enum ") ) + strip_len = 5; + else if ( HasPrefix(type_cstr, "struct ") ) + strip_len = 7; + else if ( HasPrefix(type_cstr, "union ") ) + strip_len = 6; + + if (strip_len == 0) + return type; + + type_cstr += strip_len; + while (IsWhitespace(*type_cstr) && ++type_cstr) + ; + + return ConstString(type_cstr); +} + +template +class FormatNavigator; + +template +class FormatMap +{ +public: + + typedef typename ValueType::SharedPointer ValueSP; + typedef std::map MapType; + typedef typename MapType::iterator MapIterator; + typedef bool(*CallbackType)(void*, KeyType, const ValueSP&); + + FormatMap(IFormatChangeListener* lst) : + m_map(), + m_map_mutex(Mutex::eMutexTypeRecursive), + listener(lst) + { + } + + void + Add(KeyType name, + const ValueSP& entry) + { + if (listener) + entry->GetRevision() = listener->GetCurrentRevision(); + else + entry->GetRevision() = 0; + + Mutex::Locker locker(m_map_mutex); + m_map[name] = entry; + if (listener) + listener->Changed(); + } + + bool + Delete (KeyType name) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + m_map.erase(name); + if (listener) + listener->Changed(); + return true; + } + + void + Clear () + { + Mutex::Locker locker(m_map_mutex); + m_map.clear(); + if (listener) + listener->Changed(); + } + + bool + Get(KeyType name, + ValueSP& entry) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + entry = iter->second; + return true; + } + + void + LoopThrough (CallbackType callback, void* param) + { + if (callback) + { + Mutex::Locker locker(m_map_mutex); + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) + { + KeyType type = pos->first; + if (!callback(param, type, pos->second)) + break; + } + } + } + + uint32_t + GetCount () + { + return m_map.size(); + } + + ValueSP + GetValueAtIndex (size_t index) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.begin(); + MapIterator end = m_map.end(); + while (index > 0) + { + iter++; + index--; + if (end == iter) + return ValueSP(); + } + return iter->second; + } + + KeyType + GetKeyAtIndex (size_t index) + { + Mutex::Locker locker(m_map_mutex); + MapIterator iter = m_map.begin(); + MapIterator end = m_map.end(); + while (index > 0) + { + iter++; + index--; + if (end == iter) + return KeyType(); + } + return iter->first; + } + +protected: + MapType m_map; + Mutex m_map_mutex; + IFormatChangeListener* listener; + + MapType& + map () + { + return m_map; + } + + Mutex& + mutex () + { + return m_map_mutex; + } + + friend class FormatNavigator; + friend class FormatManager; + +}; + +template +class FormatNavigator +{ +protected: + typedef FormatMap BackEndType; + +public: + typedef typename BackEndType::MapType MapType; + typedef typename MapType::iterator MapIterator; + typedef typename MapType::key_type MapKeyType; + typedef typename MapType::mapped_type MapValueType; + typedef typename BackEndType::CallbackType CallbackType; + typedef typename std::shared_ptr > SharedPointer; + + friend class TypeCategoryImpl; + + FormatNavigator(std::string name, + IFormatChangeListener* lst) : + m_format_map(lst), + m_name(name), + m_id_cs(ConstString("id")) + { + } + + void + Add (const MapKeyType &type, const MapValueType& entry) + { + Add_Impl(type, entry, (KeyType*)NULL); + } + + bool + Delete (ConstString type) + { + return Delete_Impl(type, (KeyType*)NULL); + } + + bool + Get(ValueObject& valobj, + MapValueType& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* why = NULL) + { + uint32_t value = lldb_private::eFormatterChoiceCriterionDirectChoice; + ClangASTType ast_type(valobj.GetClangType()); + bool ret = Get(valobj, ast_type, entry, use_dynamic, value); + if (ret) + entry = MapValueType(entry); + else + entry = MapValueType(); + if (why) + *why = value; + return ret; + } + + bool + Get (ConstString type, MapValueType& entry) + { + return Get_Impl(type, entry, (KeyType*)NULL); + } + + bool + GetExact (ConstString type, MapValueType& entry) + { + return GetExact_Impl(type, entry, (KeyType*)NULL); + } + + MapValueType + GetAtIndex (size_t index) + { + return m_format_map.GetValueAtIndex(index); + } + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierAtIndex (size_t index) + { + return GetTypeNameSpecifierAtIndex_Impl(index, (KeyType*)NULL); + } + + void + Clear () + { + m_format_map.Clear(); + } + + void + LoopThrough (CallbackType callback, void* param) + { + m_format_map.LoopThrough(callback,param); + } + + uint32_t + GetCount () + { + return m_format_map.GetCount(); + } + +protected: + + BackEndType m_format_map; + + std::string m_name; + + DISALLOW_COPY_AND_ASSIGN(FormatNavigator); + + ConstString m_id_cs; + + void + Add_Impl (const MapKeyType &type, const MapValueType& entry, lldb::RegularExpressionSP *dummy) + { + m_format_map.Add(type,entry); + } + + void Add_Impl (const ConstString &type, const MapValueType& entry, ConstString *dummy) + { + m_format_map.Add(GetValidTypeName_Impl(type), entry); + } + + bool + Delete_Impl (ConstString type, ConstString *dummy) + { + return m_format_map.Delete(type); + } + + bool + Delete_Impl (ConstString type, lldb::RegularExpressionSP *dummy) + { + Mutex& x_mutex = m_format_map.mutex(); + lldb_private::Mutex::Locker locker(x_mutex); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if ( ::strcmp(type.AsCString(),regex->GetText()) == 0) + { + m_format_map.map().erase(pos); + if (m_format_map.listener) + m_format_map.listener->Changed(); + return true; + } + } + return false; + } + + bool + Get_Impl (ConstString type, MapValueType& entry, ConstString *dummy) + { + return m_format_map.Get(type, entry); + } + + bool + GetExact_Impl (ConstString type, MapValueType& entry, ConstString *dummy) + { + return Get_Impl(type,entry, (KeyType*)0); + } + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierAtIndex_Impl (size_t index, ConstString *dummy) + { + ConstString key = m_format_map.GetKeyAtIndex(index); + if (key) + return lldb::TypeNameSpecifierImplSP(new TypeNameSpecifierImpl(key.AsCString(), + false)); + else + return lldb::TypeNameSpecifierImplSP(); + } + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierAtIndex_Impl (size_t index, lldb::RegularExpressionSP *dummy) + { + lldb::RegularExpressionSP regex = m_format_map.GetKeyAtIndex(index); + if (regex.get() == NULL) + return lldb::TypeNameSpecifierImplSP(); + return lldb::TypeNameSpecifierImplSP(new TypeNameSpecifierImpl(regex->GetText(), + true)); + } + + bool + Get_Impl (ConstString key, MapValueType& value, lldb::RegularExpressionSP *dummy) + { + const char* key_cstr = key.AsCString(); + if (!key_cstr) + return false; + Mutex& x_mutex = m_format_map.mutex(); + lldb_private::Mutex::Locker locker(x_mutex); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if (regex->Execute(key_cstr)) + { + value = pos->second; + return true; + } + } + return false; + } + + bool + GetExact_Impl (ConstString key, MapValueType& value, lldb::RegularExpressionSP *dummy) + { + Mutex& x_mutex = m_format_map.mutex(); + lldb_private::Mutex::Locker locker(x_mutex); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if (strcmp(regex->GetText(),key.AsCString()) == 0) + { + value = pos->second; + return true; + } + } + return false; + } + + bool + Get_BitfieldMatch (ValueObject& valobj, + ConstString typeName, + MapValueType& entry, + uint32_t& reason) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + // for bitfields, append size to the typename so one can custom format them + StreamString sstring; + sstring.Printf("%s:%d",typeName.AsCString(),valobj.GetBitfieldBitSize()); + ConstString bitfieldname = ConstString(sstring.GetData()); + if (log) + log->Printf("[Get_BitfieldMatch] appended bitfield info, final result is %s", bitfieldname.GetCString()); + if (Get(bitfieldname, entry)) + { + if (log) + log->Printf("[Get_BitfieldMatch] bitfield direct match found, returning"); + return true; + } + else + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedBitField; + if (log) + log->Printf("[Get_BitfieldMatch] no bitfield direct match"); + return false; + } + } + + bool Get_ObjC (ValueObject& valobj, + MapValueType& entry) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + lldb::ProcessSP process_sp = valobj.GetProcessSP(); + ObjCLanguageRuntime* runtime = process_sp->GetObjCLanguageRuntime(); + if (runtime == NULL) + { + if (log) + log->Printf("[Get_ObjC] no valid ObjC runtime, skipping dynamic"); + return false; + } + ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp (runtime->GetClassDescriptor(valobj)); + if (!objc_class_sp) + { + if (log) + log->Printf("[Get_ObjC] invalid ISA, skipping dynamic"); + return false; + } + ConstString name (objc_class_sp->GetClassName()); + if (log) + log->Printf("[Get_ObjC] dynamic type inferred is %s - looking for direct dynamic match", name.GetCString()); + if (Get(name, entry)) + { + if (log) + log->Printf("[Get_ObjC] direct dynamic match found, returning"); + return true; + } + if (log) + log->Printf("[Get_ObjC] no dynamic match"); + return false; + } + + bool + Get_Impl (ValueObject& valobj, + ClangASTType clang_type, + MapValueType& entry, + lldb::DynamicValueType use_dynamic, + uint32_t& reason) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + if (!clang_type.IsValid()) + { + if (log) + log->Printf("[Get_Impl] type is invalid, returning"); + return false; + } + + clang_type = clang_type.RemoveFastQualifiers(); + + ConstString typeName(clang_type.GetConstTypeName()); + + if (valobj.GetBitfieldBitSize() > 0) + { + if (Get_BitfieldMatch(valobj, typeName, entry, reason)) + return true; + } + + if (log) + log->Printf("[Get_Impl] trying to get %s for VO name %s of type %s", + m_name.c_str(), + valobj.GetName().AsCString(), + typeName.AsCString()); + + if (Get(typeName, entry)) + { + if (log) + log->Printf("[Get] direct match found, returning"); + return true; + } + if (log) + log->Printf("[Get_Impl] no direct match"); + + // strip pointers and references and see if that helps + if (clang_type.IsReferenceType()) + { + if (log) + log->Printf("[Get_Impl] stripping reference"); + if (Get_Impl(valobj, clang_type.GetNonReferenceType(), entry, use_dynamic, reason) && !entry->SkipsReferences()) + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedPointerReference; + return true; + } + } + else if (clang_type.IsPointerType()) + { + if (log) + log->Printf("[Get_Impl] stripping pointer"); + if (Get_Impl(valobj, clang_type.GetPointeeType(), entry, use_dynamic, reason) && !entry->SkipsPointers()) + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedPointerReference; + return true; + } + } + + bool canBeObjCDynamic = valobj.GetClangType().IsPossibleDynamicType (NULL, + false, // no C++ + true); // yes ObjC + + if (canBeObjCDynamic) + { + if (use_dynamic != lldb::eNoDynamicValues) + { + if (log) + log->Printf("[Get_Impl] allowed to figure out dynamic ObjC type"); + if (Get_ObjC(valobj,entry)) + { + reason |= lldb_private::eFormatterChoiceCriterionDynamicObjCDiscovery; + return true; + } + } + if (log) + log->Printf("[Get_Impl] dynamic disabled or failed - stripping ObjC pointer"); + if (Get_Impl(valobj, clang_type.GetPointeeType(), entry, use_dynamic, reason) && !entry->SkipsPointers()) + { + reason |= lldb_private::eFormatterChoiceCriterionStrippedPointerReference; + return true; + } + } + + // try to strip typedef chains + if (clang_type.IsTypedefType()) + { + if (log) + log->Printf("[Get_Impl] stripping typedef"); + if ((Get_Impl(valobj, clang_type.GetTypedefedType(), entry, use_dynamic, reason)) && entry->Cascades()) + { + reason |= lldb_private::eFormatterChoiceCriterionNavigatedTypedefs; + return true; + } + } + + // out of luck here + return false; + } + + // we are separately passing in valobj and type because the valobj is fixed (and is used for ObjC discovery and bitfield size) + // but the type can change (e.g. stripping pointers, ...) + bool Get (ValueObject& valobj, + ClangASTType clang_type, + MapValueType& entry, + lldb::DynamicValueType use_dynamic, + uint32_t& reason) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + if (Get_Impl (valobj, clang_type, entry, use_dynamic, reason)) + return true; + + // try going to the unqualified type + do { + if (log) + log->Printf("[Get] trying the unqualified type"); + if (!clang_type.IsValid()) + break; + + ClangASTType unqual_clang_ast_type = clang_type.GetFullyUnqualifiedType(); + if (!unqual_clang_ast_type.IsValid()) + { + if (log) + log->Printf("[Get] could not get the unqual_clang_ast_type"); + break; + } + if (unqual_clang_ast_type.GetOpaqueQualType() != clang_type.GetOpaqueQualType()) + { + if (log) + log->Printf("[Get] unqualified type is there and is not the same, let's try"); + if (Get_Impl (valobj, unqual_clang_ast_type,entry, use_dynamic, reason)) + return true; + } + else if (log) + log->Printf("[Get] unqualified type same as original type"); + } while(false); + + // if all else fails, go to static type + if (valobj.IsDynamic()) + { + if (log) + log->Printf("[Get] going to static value"); + lldb::ValueObjectSP static_value_sp(valobj.GetStaticValue()); + if (static_value_sp) + { + if (log) + log->Printf("[Get] has a static value - actually use it"); + if (Get(*static_value_sp.get(), static_value_sp->GetClangType(), entry, use_dynamic, reason)) + { + reason |= lldb_private::eFormatterChoiceCriterionWentToStaticValue; + return true; + } + } + } + + return false; + } +}; + +} // namespace lldb_private + +#endif // lldb_FormatNavigator_h_ diff --git a/include/lldb/DataFormatters/TypeCategory.h b/include/lldb/DataFormatters/TypeCategory.h new file mode 100644 index 00000000000..b76d84f4772 --- /dev/null +++ b/include/lldb/DataFormatters/TypeCategory.h @@ -0,0 +1,230 @@ +//===-- TypeCategory.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeCategory_h_ +#define lldb_TypeCategory_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/DataFormatters/FormatNavigator.h" + +namespace lldb_private { + class TypeCategoryImpl + { + private: + + typedef FormatNavigator SummaryNavigator; + typedef FormatNavigator RegexSummaryNavigator; + + typedef FormatNavigator FilterNavigator; + typedef FormatNavigator RegexFilterNavigator; + +#ifndef LLDB_DISABLE_PYTHON + typedef FormatNavigator SynthNavigator; + typedef FormatNavigator RegexSynthNavigator; +#endif // #ifndef LLDB_DISABLE_PYTHON + + typedef SummaryNavigator::MapType SummaryMap; + typedef RegexSummaryNavigator::MapType RegexSummaryMap; + typedef FilterNavigator::MapType FilterMap; + typedef RegexFilterNavigator::MapType RegexFilterMap; +#ifndef LLDB_DISABLE_PYTHON + typedef SynthNavigator::MapType SynthMap; + typedef RegexSynthNavigator::MapType RegexSynthMap; +#endif // #ifndef LLDB_DISABLE_PYTHON + + public: + + typedef uint16_t FormatCategoryItems; + static const uint16_t ALL_ITEM_TYPES = UINT16_MAX; + + typedef SummaryNavigator::SharedPointer SummaryNavigatorSP; + typedef RegexSummaryNavigator::SharedPointer RegexSummaryNavigatorSP; + typedef FilterNavigator::SharedPointer FilterNavigatorSP; + typedef RegexFilterNavigator::SharedPointer RegexFilterNavigatorSP; +#ifndef LLDB_DISABLE_PYTHON + typedef SynthNavigator::SharedPointer SynthNavigatorSP; + typedef RegexSynthNavigator::SharedPointer RegexSynthNavigatorSP; +#endif // #ifndef LLDB_DISABLE_PYTHON + + TypeCategoryImpl (IFormatChangeListener* clist, + ConstString name); + + SummaryNavigatorSP + GetSummaryNavigator () + { + return SummaryNavigatorSP(m_summary_nav); + } + + RegexSummaryNavigatorSP + GetRegexSummaryNavigator () + { + return RegexSummaryNavigatorSP(m_regex_summary_nav); + } + + FilterNavigatorSP + GetFilterNavigator () + { + return FilterNavigatorSP(m_filter_nav); + } + + RegexFilterNavigatorSP + GetRegexFilterNavigator () + { + return RegexFilterNavigatorSP(m_regex_filter_nav); + } + + SummaryNavigator::MapValueType + GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp); + + FilterNavigator::MapValueType + GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp); + +#ifndef LLDB_DISABLE_PYTHON + SynthNavigator::MapValueType + GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp); +#endif + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForSummaryAtIndex (size_t index); + + SummaryNavigator::MapValueType + GetSummaryAtIndex (size_t index); + + FilterNavigator::MapValueType + GetFilterAtIndex (size_t index); + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForFilterAtIndex (size_t index); + +#ifndef LLDB_DISABLE_PYTHON + SynthNavigatorSP + GetSyntheticNavigator () + { + return SynthNavigatorSP(m_synth_nav); + } + + RegexSynthNavigatorSP + GetRegexSyntheticNavigator () + { + return RegexSynthNavigatorSP(m_regex_synth_nav); + } + + SynthNavigator::MapValueType + GetSyntheticAtIndex (size_t index); + + lldb::TypeNameSpecifierImplSP + GetTypeNameSpecifierForSyntheticAtIndex (size_t index); + +#endif // #ifndef LLDB_DISABLE_PYTHON + + bool + IsEnabled () const + { + return m_enabled; + } + + uint32_t + GetEnabledPosition() + { + if (m_enabled == false) + return UINT32_MAX; + else + return m_enabled_position; + } + + bool + Get (ValueObject& valobj, + lldb::TypeSummaryImplSP& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* reason = NULL); + + bool + Get (ValueObject& valobj, + lldb::SyntheticChildrenSP& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* reason = NULL); + + void + Clear (FormatCategoryItems items = ALL_ITEM_TYPES); + + bool + Delete (ConstString name, + FormatCategoryItems items = ALL_ITEM_TYPES); + + uint32_t + GetCount (FormatCategoryItems items = ALL_ITEM_TYPES); + + const char* + GetName () + { + return m_name.GetCString(); + } + + bool + AnyMatches (ConstString type_name, + FormatCategoryItems items = ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + FormatCategoryItems* matching_type = NULL); + + typedef std::shared_ptr SharedPointer; + + private: + SummaryNavigator::SharedPointer m_summary_nav; + RegexSummaryNavigator::SharedPointer m_regex_summary_nav; + FilterNavigator::SharedPointer m_filter_nav; + RegexFilterNavigator::SharedPointer m_regex_filter_nav; +#ifndef LLDB_DISABLE_PYTHON + SynthNavigator::SharedPointer m_synth_nav; + RegexSynthNavigator::SharedPointer m_regex_synth_nav; +#endif // #ifndef LLDB_DISABLE_PYTHON + + bool m_enabled; + + IFormatChangeListener* m_change_listener; + + Mutex m_mutex; + + ConstString m_name; + + uint32_t m_enabled_position; + + void + Enable (bool value, uint32_t position); + + void + Disable () + { + Enable(false, UINT32_MAX); + } + + friend class TypeCategoryMap; + + friend class FormatNavigator; + friend class FormatNavigator; + + friend class FormatNavigator; + friend class FormatNavigator; + +#ifndef LLDB_DISABLE_PYTHON + friend class FormatNavigator; + friend class FormatNavigator; +#endif // #ifndef LLDB_DISABLE_PYTHON + }; + +} // namespace lldb_private + +#endif // lldb_TypeCategory_h_ diff --git a/include/lldb/DataFormatters/TypeCategoryMap.h b/include/lldb/DataFormatters/TypeCategoryMap.h new file mode 100644 index 00000000000..c2465ad13aa --- /dev/null +++ b/include/lldb/DataFormatters/TypeCategoryMap.h @@ -0,0 +1,148 @@ +//===-- TypeCategoryMap.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeCategoryMap_h_ +#define lldb_TypeCategoryMap_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/DataFormatters/FormatNavigator.h" +#include "lldb/DataFormatters/TypeCategory.h" + +namespace lldb_private { + class TypeCategoryMap + { + private: + typedef ConstString KeyType; + typedef TypeCategoryImpl ValueType; + typedef ValueType::SharedPointer ValueSP; + typedef std::list ActiveCategoriesList; + typedef ActiveCategoriesList::iterator ActiveCategoriesIterator; + + public: + typedef std::map MapType; + typedef MapType::iterator MapIterator; + typedef bool(*CallbackType)(void*, const ValueSP&); + typedef uint32_t Position; + + static const Position First = 0; + static const Position Default = 1; + static const Position Last = UINT32_MAX; + + TypeCategoryMap (IFormatChangeListener* lst); + + void + Add (KeyType name, + const ValueSP& entry); + + bool + Delete (KeyType name); + + bool + Enable (KeyType category_name, + Position pos = Default); + + bool + Disable (KeyType category_name); + + bool + Enable (ValueSP category, + Position pos = Default); + + bool + Disable (ValueSP category); + + void + Clear (); + + bool + Get (KeyType name, + ValueSP& entry); + + bool + Get (uint32_t pos, + ValueSP& entry); + + void + LoopThrough (CallbackType callback, void* param); + + lldb::TypeCategoryImplSP + GetAtIndex (uint32_t); + + bool + AnyMatches (ConstString type_name, + TypeCategoryImpl::FormatCategoryItems items = TypeCategoryImpl::ALL_ITEM_TYPES, + bool only_enabled = true, + const char** matching_category = NULL, + TypeCategoryImpl::FormatCategoryItems* matching_type = NULL); + + uint32_t + GetCount () + { + return m_map.size(); + } + + lldb::TypeSummaryImplSP + GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); + +#ifndef LLDB_DISABLE_PYTHON + lldb::SyntheticChildrenSP + GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic); +#endif + + private: + + class delete_matching_categories + { + lldb::TypeCategoryImplSP ptr; + public: + delete_matching_categories(lldb::TypeCategoryImplSP p) : ptr(p) + {} + + bool operator()(const lldb::TypeCategoryImplSP& other) + { + return ptr.get() == other.get(); + } + }; + + Mutex m_map_mutex; + IFormatChangeListener* listener; + + MapType m_map; + ActiveCategoriesList m_active_categories; + + MapType& map () + { + return m_map; + } + + ActiveCategoriesList& active_list () + { + return m_active_categories; + } + + Mutex& mutex () + { + return m_map_mutex; + } + + friend class FormatNavigator; + friend class FormatManager; + }; +} // namespace lldb_private + +#endif // lldb_TypeCategoryMap_h_ diff --git a/include/lldb/DataFormatters/TypeFormat.h b/include/lldb/DataFormatters/TypeFormat.h new file mode 100644 index 00000000000..77135c448ed --- /dev/null +++ b/include/lldb/DataFormatters/TypeFormat.h @@ -0,0 +1,220 @@ +//===-- TypeFormat.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeFormat_h_ +#define lldb_TypeFormat_h_ + +// C Includes + +// C++ Includes +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" + +namespace lldb_private { + class TypeFormatImpl + { + public: + class Flags + { + public: + + Flags () : + m_flags (lldb::eTypeOptionCascade) + {} + + Flags (const Flags& other) : + m_flags (other.m_flags) + {} + + Flags (uint32_t value) : + m_flags (value) + {} + + Flags& + operator = (const Flags& rhs) + { + if (&rhs != this) + m_flags = rhs.m_flags; + + return *this; + } + + Flags& + operator = (const uint32_t& rhs) + { + m_flags = rhs; + return *this; + } + + Flags& + Clear() + { + m_flags = 0; + return *this; + } + + bool + GetCascades () const + { + return (m_flags & lldb::eTypeOptionCascade) == lldb::eTypeOptionCascade; + } + + Flags& + SetCascades (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionCascade; + else + m_flags &= ~lldb::eTypeOptionCascade; + return *this; + } + + bool + GetSkipPointers () const + { + return (m_flags & lldb::eTypeOptionSkipPointers) == lldb::eTypeOptionSkipPointers; + } + + Flags& + SetSkipPointers (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipPointers; + else + m_flags &= ~lldb::eTypeOptionSkipPointers; + return *this; + } + + bool + GetSkipReferences () const + { + return (m_flags & lldb::eTypeOptionSkipReferences) == lldb::eTypeOptionSkipReferences; + } + + Flags& + SetSkipReferences (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipReferences; + else + m_flags &= ~lldb::eTypeOptionSkipReferences; + return *this; + } + + uint32_t + GetValue () + { + return m_flags; + } + + void + SetValue (uint32_t value) + { + m_flags = value; + } + + private: + uint32_t m_flags; + }; + + TypeFormatImpl (lldb::Format f = lldb::eFormatInvalid, + const Flags& flags = Flags()); + + typedef std::shared_ptr SharedPointer; + typedef bool(*ValueCallback)(void*, ConstString, const lldb::TypeFormatImplSP&); + + ~TypeFormatImpl () + { + } + + bool + Cascades () const + { + return m_flags.GetCascades(); + } + bool + SkipsPointers () const + { + return m_flags.GetSkipPointers(); + } + bool + SkipsReferences () const + { + return m_flags.GetSkipReferences(); + } + + void + SetCascades (bool value) + { + m_flags.SetCascades(value); + } + + void + SetSkipsPointers (bool value) + { + m_flags.SetSkipPointers(value); + } + + void + SetSkipsReferences (bool value) + { + m_flags.SetSkipReferences(value); + } + + lldb::Format + GetFormat () const + { + return m_format; + } + + void + SetFormat (lldb::Format fmt) + { + m_format = fmt; + } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + std::string + GetDescription(); + + protected: + Flags m_flags; + lldb::Format m_format; + uint32_t m_my_revision; + + private: + DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl); + }; +} // namespace lldb_private + +#endif // lldb_TypeFormat_h_ diff --git a/include/lldb/DataFormatters/TypeSummary.h b/include/lldb/DataFormatters/TypeSummary.h new file mode 100644 index 00000000000..2183384b9d6 --- /dev/null +++ b/include/lldb/DataFormatters/TypeSummary.h @@ -0,0 +1,547 @@ +//===-- TypeSummary.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeSummary_h_ +#define lldb_TypeSummary_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + + class TypeSummaryImpl + { + public: + class Flags + { + public: + + Flags () : + m_flags (lldb::eTypeOptionCascade) + {} + + Flags (const Flags& other) : + m_flags (other.m_flags) + {} + + Flags (uint32_t value) : + m_flags (value) + {} + + Flags& + operator = (const Flags& rhs) + { + if (&rhs != this) + m_flags = rhs.m_flags; + + return *this; + } + + Flags& + operator = (const uint32_t& rhs) + { + m_flags = rhs; + return *this; + } + + Flags& + Clear() + { + m_flags = 0; + return *this; + } + + bool + GetCascades () const + { + return (m_flags & lldb::eTypeOptionCascade) == lldb::eTypeOptionCascade; + } + + Flags& + SetCascades (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionCascade; + else + m_flags &= ~lldb::eTypeOptionCascade; + return *this; + } + + bool + GetSkipPointers () const + { + return (m_flags & lldb::eTypeOptionSkipPointers) == lldb::eTypeOptionSkipPointers; + } + + Flags& + SetSkipPointers (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipPointers; + else + m_flags &= ~lldb::eTypeOptionSkipPointers; + return *this; + } + + bool + GetSkipReferences () const + { + return (m_flags & lldb::eTypeOptionSkipReferences) == lldb::eTypeOptionSkipReferences; + } + + Flags& + SetSkipReferences (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipReferences; + else + m_flags &= ~lldb::eTypeOptionSkipReferences; + return *this; + } + + bool + GetDontShowChildren () const + { + return (m_flags & lldb::eTypeOptionHideChildren) == lldb::eTypeOptionHideChildren; + } + + Flags& + SetDontShowChildren (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideChildren; + else + m_flags &= ~lldb::eTypeOptionHideChildren; + return *this; + } + + bool + GetDontShowValue () const + { + return (m_flags & lldb::eTypeOptionHideValue) == lldb::eTypeOptionHideValue; + } + + Flags& + SetDontShowValue (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideValue; + else + m_flags &= ~lldb::eTypeOptionHideValue; + return *this; + } + + bool + GetShowMembersOneLiner () const + { + return (m_flags & lldb::eTypeOptionShowOneLiner) == lldb::eTypeOptionShowOneLiner; + } + + Flags& + SetShowMembersOneLiner (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionShowOneLiner; + else + m_flags &= ~lldb::eTypeOptionShowOneLiner; + return *this; + } + + bool + GetHideItemNames () const + { + return (m_flags & lldb::eTypeOptionHideNames) == lldb::eTypeOptionHideNames; + } + + Flags& + SetHideItemNames (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionHideNames; + else + m_flags &= ~lldb::eTypeOptionHideNames; + return *this; + } + + uint32_t + GetValue () + { + return m_flags; + } + + void + SetValue (uint32_t value) + { + m_flags = value; + } + + private: + uint32_t m_flags; + }; + + typedef enum Type + { + eTypeUnknown, + eTypeString, + eTypeScript, + eTypeCallback + } Type; + + TypeSummaryImpl (const TypeSummaryImpl::Flags& flags); + + bool + Cascades () const + { + return m_flags.GetCascades(); + } + bool + SkipsPointers () const + { + return m_flags.GetSkipPointers(); + } + bool + SkipsReferences () const + { + return m_flags.GetSkipReferences(); + } + + bool + DoesPrintChildren () const + { + return !m_flags.GetDontShowChildren(); + } + + bool + DoesPrintValue () const + { + return !m_flags.GetDontShowValue(); + } + + bool + IsOneliner () const + { + return m_flags.GetShowMembersOneLiner(); + } + + bool + HideNames () const + { + return m_flags.GetHideItemNames(); + } + + void + SetCascades (bool value) + { + m_flags.SetCascades(value); + } + + void + SetSkipsPointers (bool value) + { + m_flags.SetSkipPointers(value); + } + + void + SetSkipsReferences (bool value) + { + m_flags.SetSkipReferences(value); + } + + void + SetDoesPrintChildren (bool value) + { + m_flags.SetDontShowChildren(!value); + } + + void + SetDoesPrintValue (bool value) + { + m_flags.SetDontShowValue(!value); + } + + void + SetIsOneliner (bool value) + { + m_flags.SetShowMembersOneLiner(value); + } + + void + SetHideNames (bool value) + { + m_flags.SetHideItemNames(value); + } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + virtual + ~TypeSummaryImpl () + { + } + + // we are using a ValueObject* instead of a ValueObjectSP because we do not need to hold on to this for + // extended periods of time and we trust the ValueObject to stay around for as long as it is required + // for us to generate its summary + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest) = 0; + + virtual std::string + GetDescription () = 0; + + virtual bool + IsScripted () = 0; + + virtual Type + GetType () = 0; + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + typedef std::shared_ptr SharedPointer; + typedef bool(*SummaryCallback)(void*, ConstString, const lldb::TypeSummaryImplSP&); + typedef bool(*RegexSummaryCallback)(void*, lldb::RegularExpressionSP, const lldb::TypeSummaryImplSP&); + + protected: + uint32_t m_my_revision; + Flags m_flags; + + private: + DISALLOW_COPY_AND_ASSIGN(TypeSummaryImpl); + }; + + // simple string-based summaries, using ${var to show data + struct StringSummaryFormat : public TypeSummaryImpl + { + std::string m_format; + + StringSummaryFormat(const TypeSummaryImpl::Flags& flags, + const char* f); + + const char* + GetSummaryString () const + { + return m_format.c_str(); + } + + void + SetSummaryString (const char* data) + { + if (data) + m_format.assign(data); + else + m_format.clear(); + } + + virtual + ~StringSummaryFormat() + { + } + + virtual bool + FormatObject(ValueObject *valobj, + std::string& dest); + + virtual std::string + GetDescription(); + + virtual bool + IsScripted () + { + return false; + } + + + virtual Type + GetType () + { + return TypeSummaryImpl::eTypeString; + } + + private: + DISALLOW_COPY_AND_ASSIGN(StringSummaryFormat); + }; + + // summaries implemented via a C++ function + struct CXXFunctionSummaryFormat : public TypeSummaryImpl + { + + // we should convert these to SBValue and SBStream if we ever cross + // the boundary towards the external world + typedef bool (*Callback)(ValueObject& valobj, Stream& dest); + + Callback m_impl; + std::string m_description; + + CXXFunctionSummaryFormat (const TypeSummaryImpl::Flags& flags, + Callback impl, + const char* description); + + Callback + GetBackendFunction () const + { + return m_impl; + } + + const char* + GetTextualInfo () const + { + return m_description.c_str(); + } + + void + SetBackendFunction (Callback cb_func) + { + m_impl = cb_func; + } + + void + SetTextualInfo (const char* descr) + { + if (descr) + m_description.assign(descr); + else + m_description.clear(); + } + + virtual + ~CXXFunctionSummaryFormat () + { + } + + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest); + + virtual std::string + GetDescription (); + + virtual bool + IsScripted () + { + return false; + } + + virtual Type + GetType () + { + return TypeSummaryImpl::eTypeCallback; + } + + typedef std::shared_ptr SharedPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(CXXFunctionSummaryFormat); + }; + +#ifndef LLDB_DISABLE_PYTHON + + // Python-based summaries, running script code to show data + struct ScriptSummaryFormat : public TypeSummaryImpl + { + std::string m_function_name; + std::string m_python_script; + lldb::ScriptInterpreterObjectSP m_script_function_sp; + + ScriptSummaryFormat(const TypeSummaryImpl::Flags& flags, + const char *function_name, + const char* python_script = NULL); + + const char* + GetFunctionName () const + { + return m_function_name.c_str(); + } + + const char* + GetPythonScript () const + { + return m_python_script.c_str(); + } + + void + SetFunctionName (const char* function_name) + { + if (function_name) + m_function_name.assign(function_name); + else + m_function_name.clear(); + m_python_script.clear(); + } + + void + SetPythonScript (const char* script) + { + if (script) + m_python_script.assign(script); + else + m_python_script.clear(); + } + + virtual + ~ScriptSummaryFormat () + { + } + + virtual bool + FormatObject (ValueObject *valobj, + std::string& dest); + + virtual std::string + GetDescription (); + + virtual bool + IsScripted () + { + return true; + } + + virtual Type + GetType () + { + return TypeSummaryImpl::eTypeScript; + } + + typedef std::shared_ptr SharedPointer; + + + private: + DISALLOW_COPY_AND_ASSIGN(ScriptSummaryFormat); + }; +#endif +} // namespace lldb_private + +#endif // lldb_TypeSummary_h_ diff --git a/include/lldb/DataFormatters/TypeSynthetic.h b/include/lldb/DataFormatters/TypeSynthetic.h new file mode 100644 index 00000000000..a32f4b76117 --- /dev/null +++ b/include/lldb/DataFormatters/TypeSynthetic.h @@ -0,0 +1,594 @@ +//===-- TypeSynthetic.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_TypeSynthetic_h_ +#define lldb_TypeSynthetic_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + class SyntheticChildrenFrontEnd + { + protected: + ValueObject &m_backend; + public: + + SyntheticChildrenFrontEnd (ValueObject &backend) : + m_backend(backend) + {} + + virtual + ~SyntheticChildrenFrontEnd () + { + } + + virtual size_t + CalculateNumChildren () = 0; + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) = 0; + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) = 0; + + // this function is assumed to always succeed and it if fails, the front-end should know to deal + // with it in the correct way (most probably, by refusing to return any children) + // the return value of Update() should actually be interpreted as "ValueObjectSyntheticFilter cache is good/bad" + // if =true, ValueObjectSyntheticFilter is allowed to use the children it fetched previously and cached + // if =false, ValueObjectSyntheticFilter must throw away its cache, and query again for children + virtual bool + Update () = 0; + + // if this function returns false, then CalculateNumChildren() MUST return 0 since UI frontends + // might validly decide not to inquire for children given a false return value from this call + // if it returns true, then CalculateNumChildren() can return any number >= 0 (0 being valid) + // it should if at all possible be more efficient than CalculateNumChildren() + virtual bool + MightHaveChildren () = 0; + + typedef std::shared_ptr SharedPointer; + typedef std::unique_ptr AutoPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(SyntheticChildrenFrontEnd); + }; + + class SyntheticChildren + { + public: + + class Flags + { + public: + + Flags () : + m_flags (lldb::eTypeOptionCascade) + {} + + Flags (const Flags& other) : + m_flags (other.m_flags) + {} + + Flags (uint32_t value) : + m_flags (value) + {} + + Flags& + operator = (const Flags& rhs) + { + if (&rhs != this) + m_flags = rhs.m_flags; + + return *this; + } + + Flags& + operator = (const uint32_t& rhs) + { + m_flags = rhs; + return *this; + } + + Flags& + Clear() + { + m_flags = 0; + return *this; + } + + bool + GetCascades () const + { + return (m_flags & lldb::eTypeOptionCascade) == lldb::eTypeOptionCascade; + } + + Flags& + SetCascades (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionCascade; + else + m_flags &= ~lldb::eTypeOptionCascade; + return *this; + } + + bool + GetSkipPointers () const + { + return (m_flags & lldb::eTypeOptionSkipPointers) == lldb::eTypeOptionSkipPointers; + } + + Flags& + SetSkipPointers (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipPointers; + else + m_flags &= ~lldb::eTypeOptionSkipPointers; + return *this; + } + + bool + GetSkipReferences () const + { + return (m_flags & lldb::eTypeOptionSkipReferences) == lldb::eTypeOptionSkipReferences; + } + + Flags& + SetSkipReferences (bool value = true) + { + if (value) + m_flags |= lldb::eTypeOptionSkipReferences; + else + m_flags &= ~lldb::eTypeOptionSkipReferences; + return *this; + } + + uint32_t + GetValue () + { + return m_flags; + } + + void + SetValue (uint32_t value) + { + m_flags = value; + } + + private: + uint32_t m_flags; + }; + + SyntheticChildren (const Flags& flags) : + m_flags(flags) + { + } + + virtual + ~SyntheticChildren () + { + } + + bool + Cascades () const + { + return m_flags.GetCascades(); + } + bool + SkipsPointers () const + { + return m_flags.GetSkipPointers(); + } + bool + SkipsReferences () const + { + return m_flags.GetSkipReferences(); + } + + void + SetCascades (bool value) + { + m_flags.SetCascades(value); + } + + void + SetSkipsPointers (bool value) + { + m_flags.SetSkipPointers(value); + } + + void + SetSkipsReferences (bool value) + { + m_flags.SetSkipReferences(value); + } + + uint32_t + GetOptions () + { + return m_flags.GetValue(); + } + + void + SetOptions (uint32_t value) + { + m_flags.SetValue(value); + } + + virtual bool + IsScripted () = 0; + + virtual std::string + GetDescription () = 0; + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd (ValueObject &backend) = 0; + + typedef std::shared_ptr SharedPointer; + typedef bool(*SyntheticChildrenCallback)(void*, ConstString, const SyntheticChildren::SharedPointer&); + + uint32_t& + GetRevision () + { + return m_my_revision; + } + + protected: + uint32_t m_my_revision; + Flags m_flags; + + private: + DISALLOW_COPY_AND_ASSIGN(SyntheticChildren); + }; + + class TypeFilterImpl : public SyntheticChildren + { + std::vector m_expression_paths; + public: + TypeFilterImpl(const SyntheticChildren::Flags& flags) : + SyntheticChildren(flags), + m_expression_paths() + { + } + + TypeFilterImpl(const SyntheticChildren::Flags& flags, + const std::initializer_list items) : + SyntheticChildren(flags), + m_expression_paths() + { + for (auto path : items) + AddExpressionPath (path); + } + + void + AddExpressionPath (const char* path) + { + AddExpressionPath(std::string(path)); + } + + void + Clear() + { + m_expression_paths.clear(); + } + + size_t + GetCount() const + { + return m_expression_paths.size(); + } + + const char* + GetExpressionPathAtIndex(size_t i) const + { + return m_expression_paths[i].c_str(); + } + + bool + SetExpressionPathAtIndex (size_t i, const char* path) + { + return SetExpressionPathAtIndex(i, std::string(path)); + } + + void + 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 + 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; + } + + bool + IsScripted () + { + return false; + } + + std::string + GetDescription (); + + class FrontEnd : public SyntheticChildrenFrontEnd + { + private: + TypeFilterImpl* filter; + public: + + FrontEnd(TypeFilterImpl* flt, + ValueObject &backend) : + SyntheticChildrenFrontEnd(backend), + filter(flt) + {} + + virtual + ~FrontEnd () + { + } + + virtual size_t + CalculateNumChildren () + { + return filter->GetCount(); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + if (idx >= filter->GetCount()) + return lldb::ValueObjectSP(); + return m_backend.GetSyntheticExpressionPathChild(filter->GetExpressionPathAtIndex(idx), true); + } + + virtual bool + Update() { return false; } + + virtual bool + MightHaveChildren () + { + return filter->GetCount() > 0; + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + const char* name_cstr = name.GetCString(); + 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 (!::strcmp(name_cstr, expr_cstr)) + return i; + } + return UINT32_MAX; + } + + typedef std::shared_ptr SharedPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(FrontEnd); + }; + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd(ValueObject &backend) + { + return SyntheticChildrenFrontEnd::AutoPointer(new FrontEnd(this, backend)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TypeFilterImpl); + }; + + class CXXSyntheticChildren : public SyntheticChildren + { + public: + typedef SyntheticChildrenFrontEnd* (*CreateFrontEndCallback) (CXXSyntheticChildren*, lldb::ValueObjectSP); + protected: + CreateFrontEndCallback m_create_callback; + std::string m_description; + public: + CXXSyntheticChildren (const SyntheticChildren::Flags& flags, + const char* description, + CreateFrontEndCallback callback) : + SyntheticChildren(flags), + m_create_callback(callback), + m_description(description ? description : "") + { + } + + bool + IsScripted () + { + return false; + } + + std::string + GetDescription (); + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd (ValueObject &backend) + { + return SyntheticChildrenFrontEnd::AutoPointer(m_create_callback(this, backend.GetSP())); + } + + private: + DISALLOW_COPY_AND_ASSIGN(CXXSyntheticChildren); + }; + +#ifndef LLDB_DISABLE_PYTHON + + class ScriptedSyntheticChildren : public SyntheticChildren + { + std::string m_python_class; + std::string m_python_code; + public: + + ScriptedSyntheticChildren (const SyntheticChildren::Flags& flags, + const char* pclass, + const char* pcode = NULL) : + SyntheticChildren(flags), + m_python_class(), + m_python_code() + { + if (pclass) + m_python_class = pclass; + if (pcode) + m_python_code = pcode; + } + + const char* + GetPythonClassName () + { + return m_python_class.c_str(); + } + + const char* + GetPythonCode () + { + return m_python_code.c_str(); + } + + void + SetPythonClassName (const char* fname) + { + m_python_class.assign(fname); + m_python_code.clear(); + } + + void + SetPythonCode (const char* script) + { + m_python_code.assign(script); + } + + std::string + GetDescription (); + + bool + IsScripted () + { + return true; + } + + class FrontEnd : public SyntheticChildrenFrontEnd + { + private: + std::string m_python_class; + lldb::ScriptInterpreterObjectSP m_wrapper_sp; + ScriptInterpreter *m_interpreter; + public: + + FrontEnd (std::string pclass, + ValueObject &backend); + + virtual + ~FrontEnd (); + + virtual size_t + CalculateNumChildren () + { + if (!m_wrapper_sp || m_interpreter == NULL) + return 0; + return m_interpreter->CalculateNumChildren(m_wrapper_sp); + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx); + + virtual bool + Update () + { + if (!m_wrapper_sp || m_interpreter == NULL) + return false; + + return m_interpreter->UpdateSynthProviderInstance(m_wrapper_sp); + } + + virtual bool + MightHaveChildren () + { + if (!m_wrapper_sp || m_interpreter == NULL) + return false; + + return m_interpreter->MightHaveChildrenSynthProviderInstance(m_wrapper_sp); + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + if (!m_wrapper_sp || m_interpreter == NULL) + return UINT32_MAX; + return m_interpreter->GetIndexOfChildWithName(m_wrapper_sp, name.GetCString()); + } + + typedef std::shared_ptr SharedPointer; + + private: + DISALLOW_COPY_AND_ASSIGN(FrontEnd); + }; + + virtual SyntheticChildrenFrontEnd::AutoPointer + GetFrontEnd(ValueObject &backend) + { + return SyntheticChildrenFrontEnd::AutoPointer(new FrontEnd(m_python_class, backend)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(ScriptedSyntheticChildren); + }; +#endif +} // namespace lldb_private + +#endif // lldb_TypeSynthetic_h_ diff --git a/include/lldb/Expression/ASTDumper.h b/include/lldb/Expression/ASTDumper.h new file mode 100644 index 00000000000..47f7ea460b8 --- /dev/null +++ b/include/lldb/Expression/ASTDumper.h @@ -0,0 +1,43 @@ +//===-- ASTDumper.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTDumper_h_ +#define liblldb_ASTDumper_h_ + +#include "clang/AST/DeclVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeVisitor.h" + +#include "lldb/Core/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::clang_type_t type); + ASTDumper (const ClangASTType &clang_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/include/lldb/Expression/ASTResultSynthesizer.h b/include/lldb/Expression/ASTResultSynthesizer.h new file mode 100644 index 00000000000..79709de3546 --- /dev/null +++ b/include/lldb/Expression/ASTResultSynthesizer.h @@ -0,0 +1,184 @@ +//===-- ASTResultSynthesizer.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTResultSynthesizer_h_ +#define liblldb_ASTResultSynthesizer_h_ + +#include "clang/Sema/SemaConsumer.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/TaggedASTType.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ASTResultSynthesizer ASTResultSynthesizer.h "lldb/Expression/ASTResultSynthesizer.h" +/// @brief 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] target + /// The target, which contains the persistent variable store and the + /// AST importer. + //---------------------------------------------------------------------- + ASTResultSynthesizer(clang::ASTConsumer *passthrough, + Target &target); + + //---------------------------------------------------------------------- + /// Destructor + //---------------------------------------------------------------------- + ~ASTResultSynthesizer(); + + //---------------------------------------------------------------------- + /// 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); + + //---------------------------------------------------------------------- + /// 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); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTranslationUnit(clang::ASTContext &Ctx); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTagDeclDefinition(clang::TagDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void CompleteTentativeDefinition(clang::VarDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleVTable(clang::CXXRecordDecl *RD, bool DefinitionRequired); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void PrintStats(); + + //---------------------------------------------------------------------- + /// 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); + + //---------------------------------------------------------------------- + /// Reset the Sema to NULL now that transformations are done + //---------------------------------------------------------------------- + void ForgetSema(); +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); + + 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. + Target &m_target; ///< The target, which contains the persistent variable store and the + clang::Sema *m_sema; ///< The Sema to use. +}; + +} + +#endif diff --git a/include/lldb/Expression/ASTStructExtractor.h b/include/lldb/Expression/ASTStructExtractor.h new file mode 100644 index 00000000000..a1518de83d6 --- /dev/null +++ b/include/lldb/Expression/ASTStructExtractor.h @@ -0,0 +1,156 @@ +//===-- ASTStructExtractor.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ASTStructExtractor_h_ +#define liblldb_ASTStructExtractor_h_ + +#include "clang/Sema/SemaConsumer.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangFunction.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ASTStructExtractor ASTStructExtractor.h "lldb/Expression/ASTStructExtractor.h" +/// @brief Extracts and describes the argument structure for a wrapped function. +/// +/// This pass integrates with ClangFunction, which calls functions with custom +/// sets of arguments. To avoid having to implement the full calling convention +/// for the target's architecture, ClangFunction 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 funtion 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. ClangFunction friends ASTStructExtractor + /// for this purpose. + //---------------------------------------------------------------------- + ASTStructExtractor(clang::ASTConsumer *passthrough, + const char *struct_name, + ClangFunction &function); + + //---------------------------------------------------------------------- + /// Destructor + //---------------------------------------------------------------------- + virtual ~ASTStructExtractor(); + + //---------------------------------------------------------------------- + /// 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); + + //---------------------------------------------------------------------- + /// 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); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTranslationUnit(clang::ASTContext &Ctx); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleTagDeclDefinition(clang::TagDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void CompleteTentativeDefinition(clang::VarDecl *D); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void HandleVTable(clang::CXXRecordDecl *RD, bool DefinitionRequired); + + //---------------------------------------------------------------------- + /// Passthrough stub + //---------------------------------------------------------------------- + void PrintStats(); + + //---------------------------------------------------------------------- + /// 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); + + //---------------------------------------------------------------------- + /// Reset the Sema to NULL now that transformations are done + //---------------------------------------------------------------------- + void ForgetSema(); +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. + + ClangFunction &m_function; ///< The function to populate with information about the argument structure. + std::string m_struct_name; ///< The name of the structure to extract. +}; + +} + +#endif diff --git a/include/lldb/Expression/ClangASTSource.h b/include/lldb/Expression/ClangASTSource.h new file mode 100644 index 00000000000..3e41a9e69e9 --- /dev/null +++ b/include/lldb/Expression/ClangASTSource.h @@ -0,0 +1,530 @@ +//===-- ClangASTSource.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTSource_h_ +#define liblldb_ClangASTSource_h_ + +#include + +#include "clang/Basic/IdentifierTable.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Target/Target.h" + +#include "llvm/ADT/SmallSet.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ClangASTSource ClangASTSource.h "lldb/Expression/ClangASTSource.h" +/// @brief 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] declMap + /// A reference to the LLDB object that handles entity lookup. + //------------------------------------------------------------------ + ClangASTSource (const lldb::TargetSP &target) : + m_import_in_progress (false), + m_lookups_enabled (false), + m_target (target), + m_ast_context (NULL), + m_active_lookups () + { + m_ast_importer = m_target->GetClangASTImporter(); + } + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~ClangASTSource(); + + //------------------------------------------------------------------ + /// Interface stubs. + //------------------------------------------------------------------ + clang::Decl *GetExternalDecl (uint32_t) { return NULL; } + clang::Stmt *GetExternalDeclStmt (uint64_t) { return NULL; } + clang::Selector GetExternalSelector (uint32_t) { return clang::Selector(); } + uint32_t GetNumExternalSelectors () { return 0; } + clang::CXXBaseSpecifier *GetExternalCXXBaseSpecifiers (uint64_t Offset) + { return NULL; } + void MaterializeVisibleDecls (const clang::DeclContext *DC) + { return; } + + void InstallASTContext (clang::ASTContext *ast_context) + { + m_ast_context = ast_context; + m_ast_importer->InstallMapCompleter(ast_context, *this); + } + + // + // 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); + + //------------------------------------------------------------------ + /// Enumerate all Decls in a given lexical context. + /// + /// @param[in] DC + /// The DeclContext being searched. + /// + /// @param[in] isKindWeWant + /// If non-NULL, 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. + //------------------------------------------------------------------ + clang::ExternalLoadResult + FindExternalLexicalDecls (const clang::DeclContext *DC, + bool (*isKindWeWant)(clang::Decl::Kind), + llvm::SmallVectorImpl &Decls); + + //------------------------------------------------------------------ + /// 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 &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets); + + //------------------------------------------------------------------ + /// Complete a TagDecl. + /// + /// @param[in] Tag + /// The Decl to be completed in place. + //------------------------------------------------------------------ + virtual void + CompleteType (clang::TagDecl *Tag); + + //------------------------------------------------------------------ + /// Complete an ObjCInterfaceDecl. + /// + /// @param[in] Class + /// The Decl to be completed in place. + //------------------------------------------------------------------ + virtual void + CompleteType (clang::ObjCInterfaceDecl *Class); + + //------------------------------------------------------------------ + /// 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); + + // + // 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, + const ConstString &name, + ClangASTImporter::NamespaceMapSP &parent_map) const; + + // + // 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); + + 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" + /// @brief 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) + { + return m_original.FindExternalVisibleDeclsByName(DC, Name); + } + + clang::ExternalLoadResult + FindExternalLexicalDecls (const clang::DeclContext *DC, + bool (*isKindWeWant)(clang::Decl::Kind), + llvm::SmallVectorImpl &Decls) + { + return m_original.FindExternalLexicalDecls(DC, isKindWeWant, Decls); + } + + void + CompleteType (clang::TagDecl *Tag) + { + return m_original.CompleteType(Tag); + } + + void + CompleteType (clang::ObjCInterfaceDecl *Class) + { + return m_original.CompleteType(Class); + } + + bool + layoutRecordType(const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets) + { + return m_original.layoutRecordType(Record, + Size, + Alignment, + FieldOffsets, + BaseOffsets, + VirtualBaseOffsets); + } + + void StartTranslationUnit (clang::ASTConsumer *Consumer) + { + 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 (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. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + void + FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module, + ClangNamespaceDecl &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] clang_type + /// The source type. + /// + /// @return + /// The imported type. + //------------------------------------------------------------------ + ClangASTType + GuardedCopyType (const ClangASTType &src_type); + + 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. + ClangASTImporter *m_ast_importer; ///< The target's AST importer. + std::set m_active_lookups; +}; + +//---------------------------------------------------------------------- +/// @class NameSearchContext ClangASTSource.h "lldb/Expression/ClangASTSource.h" +/// @brief 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 &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 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; + } 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 &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 ClangASTType &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. + //------------------------------------------------------------------ + clang::NamedDecl *AddFunDecl(const ClangASTType &type); + + //------------------------------------------------------------------ + /// 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] type + /// The opaque QualType for the TypeDecl being registered. + //------------------------------------------------------------------ + clang::NamedDecl *AddTypeDecl(const ClangASTType &clang_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::DeclContextLookupConstResult 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); +}; + +} + +#endif diff --git a/include/lldb/Expression/ClangExpression.h b/include/lldb/Expression/ClangExpression.h new file mode 100644 index 00000000000..6e831e4471e --- /dev/null +++ b/include/lldb/Expression/ClangExpression.h @@ -0,0 +1,153 @@ +//===-- ClangExpression.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpression_h_ +#define liblldb_ClangExpression_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class RecordingMemoryManager; + +//---------------------------------------------------------------------- +/// @class ClangExpression ClangExpression.h "lldb/Expression/ClangExpression.h" +/// @brief 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. ClangExpression 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 ClangExpression +{ +public: + enum ResultType { + eResultTypeAny, + eResultTypeId + }; + + ClangExpression () : + m_jit_process_wp(), + m_jit_start_addr (LLDB_INVALID_ADDRESS), + m_jit_end_addr (LLDB_INVALID_ADDRESS) + { + } + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~ClangExpression () + { + } + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + virtual const char * + Text () = 0; + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + virtual const char * + FunctionName () = 0; + + //------------------------------------------------------------------ + /// Return the language that should be used when parsing. To use + /// the default, return eLanguageTypeUnknown. + //------------------------------------------------------------------ + virtual lldb::LanguageType + Language () + { + return lldb::eLanguageTypeUnknown; + } + + //------------------------------------------------------------------ + /// 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; + + //------------------------------------------------------------------ + /// Return the desired result type of the function, or + /// eResultTypeAny if indifferent. + //------------------------------------------------------------------ + virtual ResultType + DesiredResultType () + { + return eResultTypeAny; + } + + //------------------------------------------------------------------ + /// Flags + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + virtual bool + NeedsValidation () = 0; + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + virtual bool + NeedsVariableResolution () = 0; + + //------------------------------------------------------------------ + /// Return the address of the function's JIT-compiled code, or + /// LLDB_INVALID_ADDRESS if the function is not JIT compiled + //------------------------------------------------------------------ + lldb::addr_t + StartAddress () + { + return m_jit_start_addr; + } + +protected: + + lldb::ProcessWP m_jit_process_wp; + lldb::addr_t m_jit_start_addr; ///< The address of the JITted function within the JIT allocation. LLDB_INVALID_ADDRESS if invalid. + lldb::addr_t m_jit_end_addr; ///< The address of the JITted function within the JIT allocation. LLDB_INVALID_ADDRESS if invalid. + +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpression_h_ diff --git a/include/lldb/Expression/ClangExpressionDeclMap.h b/include/lldb/Expression/ClangExpressionDeclMap.h new file mode 100644 index 00000000000..b2a43e0ac75 --- /dev/null +++ b/include/lldb/Expression/ClangExpressionDeclMap.h @@ -0,0 +1,698 @@ +//===-- ClangExpressionDeclMap.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpressionDeclMap_h_ +#define liblldb_ClangExpressionDeclMap_h_ + +// C Includes +#include +#include + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/DenseMap.h" +#include "clang/AST/Decl.h" +#include "lldb/lldb-public.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ClangExpressionDeclMap ClangExpressionDeclMap.h "lldb/Expression/ClangExpressionDeclMap.h" +/// @brief 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] exe_ctx + /// The execution context to use when parsing. + //------------------------------------------------------------------ + ClangExpressionDeclMap (bool keep_result_in_memory, + ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~ClangExpressionDeclMap (); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// [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, + const 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, + const ConstString &name, + llvm::Value *value, + size_t size, + off_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, + off_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, + off_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 function given nothing + /// but its name. Some functions are needed but didn't get Decls made + /// during parsing -- specifically, sel_registerName is never called + /// in the generated IR but we need to call it nonetheless. + /// + /// @param[in] name + /// The name of the function. + /// + /// @param[out] ptr + /// The absolute address of the function in the target. + /// + /// @return + /// True if the address could be retrieved; false otherwise. + //------------------------------------------------------------------ + bool + GetFunctionAddress (const ConstString &name, + 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. + /// + /// @return + /// Valid load address for the symbol + //------------------------------------------------------------------ + lldb::addr_t + GetSymbolAddress (Target &target, + Process *process, + const ConstString &name, + lldb::SymbolType symbol_type); + + lldb::addr_t + GetSymbolAddress (const 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); + + //------------------------------------------------------------------ + /// 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] name + /// The name as a plain C string. The NameSearchContext contains + /// a DeclarationName for the name so at first the name may seem + /// redundant, but ClangExpressionDeclMap operates in RTTI land so + /// it can't access DeclarationName. + /// + /// @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, + ClangNamespaceDecl &namespace_decl, + unsigned int current_id); +private: + ClangExpressionVariableList m_found_entities; ///< All entities that were looked up for the parser. + ClangExpressionVariableList 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. + + //---------------------------------------------------------------------- + /// The following values should not live beyond parsing + //---------------------------------------------------------------------- + class ParserVars + { + public: + ParserVars(ClangExpressionDeclMap &decl_map) : + m_exe_ctx(), + m_sym_ctx(), + m_persistent_vars(NULL), + m_enable_lookups(false), + m_materializer(NULL), + m_decl_map(decl_map) + { + } + + 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 NULL; + } + + 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; ///< The persistent variables for the process. + bool m_enable_lookups; ///< 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; ///< If non-NULL, the materializer to use when reporting used variables. + private: + ClangExpressionDeclMap &m_decl_map; + DISALLOW_COPY_AND_ASSIGN (ParserVars); + }; + + std::unique_ptr m_parser_vars; + + //---------------------------------------------------------------------- + /// Activate parser-specific variables + //---------------------------------------------------------------------- + void + EnableParserVars() + { + if (!m_parser_vars.get()) + m_parser_vars.reset(new ParserVars(*this)); + } + + //---------------------------------------------------------------------- + /// 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(NULL, NULL) + { + } + + off_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 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 data symbol that has the given name. + /// + /// @param[in] target + /// The target to use as the basis for the search. + /// + /// @param[in] name + /// The name as a plain C string. + /// + /// @return + /// The LLDB Symbol found, or NULL if none was found. + //--------------------------------------------------------- + const Symbol * + FindGlobalDataSymbol (Target &target, + const ConstString &name); + + //------------------------------------------------------------------ + /// 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, + const ConstString &name, + ClangNamespaceDecl *namespace_decl, + TypeFromUser *type = NULL); + + //------------------------------------------------------------------ + /// 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 = NULL, + TypeFromParser *parser_type = NULL); + + //------------------------------------------------------------------ + /// 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::ClangExpressionVariableSP &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, + TypeFromUser &type, + unsigned int current_id); + + //------------------------------------------------------------------ + /// Copy a C++ class type into the parser's AST context and add a + /// member function declaration to it for the expression. + /// + /// @param[in] type + /// The type that needs to be created. + //------------------------------------------------------------------ + + TypeFromParser + CopyClassType(TypeFromUser &type, + unsigned int current_id); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionDeclMap_h_ diff --git a/include/lldb/Expression/ClangExpressionParser.h b/include/lldb/Expression/ClangExpressionParser.h new file mode 100644 index 00000000000..3247f2094ba --- /dev/null +++ b/include/lldb/Expression/ClangExpressionParser.h @@ -0,0 +1,151 @@ +//===-- ClangExpressionParser.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpressionParser_h_ +#define liblldb_ClangExpressionParser_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Error.h" +#include "lldb/Expression/IRForTarget.h" + +#include +#include + +namespace lldb_private +{ + +class IRExecutionUnit; + +//---------------------------------------------------------------------- +/// @class ClangExpressionParser ClangExpressionParser.h "lldb/Expression/ClangExpressionParser.h" +/// @brief 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: + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variabes. + /// + /// @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. + //------------------------------------------------------------------ + ClangExpressionParser (ExecutionContextScope *exe_scope, + ClangExpression &expr); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~ClangExpressionParser (); + + //------------------------------------------------------------------ + /// Parse a single expression and convert it to IR using Clang. Don't + /// wrap the expression in anything at all. + /// + /// @param[in] stream + /// The stream to print errors to. + /// + /// @return + /// The number of errors encountered during parsing. 0 means + /// success. + //------------------------------------------------------------------ + unsigned + Parse (Stream &stream); + + //------------------------------------------------------------------ + /// 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_ap + /// After parsing, ownership of the execution unit for + /// for the expression is handed to this unique 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(). + //------------------------------------------------------------------ + Error + PrepareForExecution (lldb::addr_t &func_addr, + lldb::addr_t &func_end, + std::unique_ptr &execution_unit_ap, + ExecutionContext &exe_ctx, + bool &can_interpret, + lldb_private::ExecutionPolicy execution_policy); + + //------------------------------------------------------------------ + /// Disassemble the machine code for a JITted function from the target + /// process's memory and print the result to a stream. + /// + /// @param[in] stream + /// The stream to print disassembly to. + /// + /// @param[in] exc_context + /// The execution context to get the machine code from. + /// + /// @return + /// The error generated. If .Success() is true, disassembly succeeded. + //------------------------------------------------------------------ + Error + DisassembleFunction (Stream &stream, + ExecutionContext &exe_ctx); + +private: + ClangExpression & m_expr; ///< The expression to be parsed + std::unique_ptr m_llvm_context; ///< The LLVM context to generate IR into + std::unique_ptr m_file_manager; ///< The Clang file manager object used by the compiler + std::unique_ptr m_compiler; ///< The Clang compiler used to parse expressions into IR + std::unique_ptr m_builtin_context; ///< Context for Clang built-ins + std::unique_ptr m_selector_table; ///< Selector table for Objective-C methods + std::unique_ptr m_ast_context; ///< The AST context used to hold types and names for the parser + std::unique_ptr m_code_generator; ///< The Clang object that generates IR + std::unique_ptr m_execution_unit; ///< The container for the finished Module +}; + +} + +#endif // liblldb_ClangExpressionParser_h_ diff --git a/include/lldb/Expression/ClangExpressionVariable.h b/include/lldb/Expression/ClangExpressionVariable.h new file mode 100644 index 00000000000..620e604fb18 --- /dev/null +++ b/include/lldb/Expression/ClangExpressionVariable.h @@ -0,0 +1,451 @@ +//===-- ClangExpressionVariable.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExpressionVariable_h_ +#define liblldb_ClangExpressionVariable_h_ + +// C Includes +#include +#include +#include + +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Value.h" +#include "lldb/Symbol/TaggedASTType.h" + +namespace llvm { + class Value; +} + +namespace lldb_private { + +class ClangExpressionVariableList; +class ValueObjectConstResult; + +//---------------------------------------------------------------------- +/// @class ClangExpressionVariable ClangExpressionVariable.h "lldb/Expression/ClangExpressionVariable.h" +/// @brief 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: + ClangExpressionVariable(ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size); + + ClangExpressionVariable(const lldb::ValueObjectSP &valobj_sp); + + //---------------------------------------------------------------------- + /// 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); + + lldb::ValueObjectSP + GetValueObject(); + + //---------------------------------------------------------------------- + /// The following values should not live beyond parsing + //---------------------------------------------------------------------- + class ParserVars + { + public: + + ParserVars() : + m_parser_type(), + m_named_decl (NULL), + m_llvm_value (NULL), + m_lldb_value (), + m_lldb_var (), + m_lldb_sym (NULL) + { + } + + 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 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 NULL; + 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) + { + } + + off_t m_alignment; ///< The required alignment of the variable, in bytes + size_t m_size; ///< The space required for the variable, in bytes + off_t m_offset; ///< The offset of the variable in the struct, in bytes + }; + +private: + typedef std::map 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 NULL; + else + return &i->second; + } + + //---------------------------------------------------------------------- + /// Return the variable's size in bytes + //---------------------------------------------------------------------- + size_t + GetByteSize (); + + const ConstString & + GetName(); + + RegisterInfo * + GetRegisterInfo(); + + void + SetRegisterInfo (const RegisterInfo *reg_info); + + ClangASTType + GetClangType (); + + void + SetClangType (const ClangASTType &clang_type); + + TypeFromUser + GetTypeFromUser (); + + uint8_t * + GetValueBytes (); + + void + SetName (const ConstString &name); + + void + ValueUpdated (); + + // this function is used to copy the address-of m_live_sp into m_frozen_sp + // this is necessary because the results of certain cast and pointer-arithmetic + // operations (such as those described in bugzilla issues 11588 and 11618) generate + // frozen objcts that do not have a valid address-of, which can be troublesome when + // using synthetic children providers. transferring the address-of the live object + // solves these issues and provides the expected user-level behavior + void + TransferAddress (bool force = false); + + typedef std::shared_ptr ValueObjectConstResultSP; + + //---------------------------------------------------------------------- + /// Members + //---------------------------------------------------------------------- + enum Flags + { + EVNone = 0, + EVIsLLDBAllocated = 1 << 0, ///< This variable is resident in a location specifically allocated for it by LLDB in the target process + EVIsProgramReference = 1 << 1, ///< This variable is a reference to a (possibly invalid) area managed by the target program + EVNeedsAllocation = 1 << 2, ///< Space for this variable has yet to be allocated in the target process + EVIsFreezeDried = 1 << 3, ///< This variable's authoritative version is in m_frozen_sp (for example, for statically-computed results) + EVNeedsFreezeDry = 1 << 4, ///< Copy from m_live_sp to m_frozen_sp during dematerialization + EVKeepInTarget = 1 << 5, ///< Keep the allocation after the expression is complete rather than freeze drying its contents and freeing it + EVTypeIsReference = 1 << 6, ///< The original type of this variable is a reference, so materialize the value rather than the location + EVUnknownType = 1 << 7, ///< This is a symbol of unknown type, and the type must be resolved after parsing is complete + EVBareRegister = 1 << 8 ///< This variable is a direct reference to $pc or some other entity. + }; + + typedef uint16_t FlagType; + + FlagType m_flags; // takes elements of Flags + + lldb::ValueObjectSP m_frozen_sp; + lldb::ValueObjectSP m_live_sp; + + DISALLOW_COPY_AND_ASSIGN (ClangExpressionVariable); +}; + +//---------------------------------------------------------------------- +/// @class ClangExpressionVariableListBase ClangExpressionVariable.h "lldb/Expression/ClangExpressionVariable.h" +/// @brief A list of variable references. +/// +/// This class stores variables internally, acting as the permanent store. +//---------------------------------------------------------------------- +class ClangExpressionVariableList +{ +public: + //---------------------------------------------------------------------- + /// Implementation of methods in ClangExpressionVariableListBase + //---------------------------------------------------------------------- + size_t + GetSize() + { + return m_variables.size(); + } + + lldb::ClangExpressionVariableSP + GetVariableAtIndex(size_t index) + { + lldb::ClangExpressionVariableSP var_sp; + if (index < m_variables.size()) + var_sp = m_variables[index]; + return var_sp; + } + + size_t + AddVariable (const lldb::ClangExpressionVariableSP &var_sp) + { + m_variables.push_back(var_sp); + return m_variables.size() - 1; + } + + bool + ContainsVariable (const lldb::ClangExpressionVariableSP &var_sp) + { + const size_t size = m_variables.size(); + for (size_t index = 0; index < size; ++index) + { + if (m_variables[index].get() == var_sp.get()) + return true; + } + return false; + } + + //---------------------------------------------------------------------- + /// Finds a variable by name 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. + //---------------------------------------------------------------------- + lldb::ClangExpressionVariableSP + GetVariable (const ConstString &name) + { + lldb::ClangExpressionVariableSP var_sp; + for (size_t index = 0, size = GetSize(); index < size; ++index) + { + var_sp = GetVariableAtIndex(index); + if (var_sp->GetName() == name) + return var_sp; + } + var_sp.reset(); + return var_sp; + } + + lldb::ClangExpressionVariableSP + GetVariable (const char *name) + { + lldb::ClangExpressionVariableSP var_sp; + if (name && name[0]) + { + for (size_t index = 0, size = GetSize(); index < size; ++index) + { + var_sp = GetVariableAtIndex(index); + const char *var_name_cstr = var_sp->GetName().GetCString(); + if (!var_name_cstr || !name) + continue; + if (::strcmp (var_name_cstr, name) == 0) + return var_sp; + } + var_sp.reset(); + } + return var_sp; + } + + //---------------------------------------------------------------------- + /// 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. + //---------------------------------------------------------------------- + lldb::ClangExpressionVariableSP + GetVariable (const clang::NamedDecl *decl, uint64_t parser_id) + { + lldb::ClangExpressionVariableSP var_sp; + for (size_t index = 0, size = GetSize(); index < size; ++index) + { + var_sp = GetVariableAtIndex(index); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(parser_id); + + if (parser_vars && parser_vars->m_named_decl == decl) + return var_sp; + } + var_sp.reset(); + return var_sp; + } + + //---------------------------------------------------------------------- + /// Create a new variable in the list and return its index + //---------------------------------------------------------------------- + lldb::ClangExpressionVariableSP + CreateVariable (ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size) + { + lldb::ClangExpressionVariableSP var_sp(new ClangExpressionVariable(exe_scope, byte_order, addr_byte_size)); + m_variables.push_back(var_sp); + return var_sp; + } + + lldb::ClangExpressionVariableSP + CreateVariable(const lldb::ValueObjectSP &valobj_sp) + { + lldb::ClangExpressionVariableSP var_sp(new ClangExpressionVariable(valobj_sp)); + m_variables.push_back(var_sp); + return var_sp; + } + + lldb::ClangExpressionVariableSP + CreateVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size) + { + lldb::ClangExpressionVariableSP var_sp(new ClangExpressionVariable(exe_scope, byte_order, addr_byte_size)); + var_sp->SetName (name); + var_sp->SetClangType (user_type); + m_variables.push_back(var_sp); + return var_sp; + } + + void + RemoveVariable (lldb::ClangExpressionVariableSP var_sp) + { + for (std::vector::iterator vi = m_variables.begin(), ve = m_variables.end(); + vi != ve; + ++vi) + { + if (vi->get() == var_sp.get()) + { + m_variables.erase(vi); + return; + } + } + } + + void + Clear() + { + m_variables.clear(); + } + +private: + std::vector m_variables; +}; + + +} // namespace lldb_private + +#endif // liblldb_ClangExpressionVariable_h_ diff --git a/include/lldb/Expression/ClangFunction.h b/include/lldb/Expression/ClangFunction.h new file mode 100644 index 00000000000..3f96f7bd311 --- /dev/null +++ b/include/lldb/Expression/ClangFunction.h @@ -0,0 +1,652 @@ +//===-- ClangFunction.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_ClangFunction_h_ +#define lldb_ClangFunction_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Target/Process.h" + +namespace lldb_private +{ + +class ASTStructExtractor; +class ClangExpressionParser; + +//---------------------------------------------------------------------- +/// @class ClangFunction ClangFunction.h "lldb/Expression/ClangFunction.h" +/// @brief Encapsulates a function that can be called. +/// +/// A given ClangFunction 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 ClangFunction 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 ClangFunction : public ClangExpression +{ + friend class ASTStructExtractor; +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] exe_scope + /// An execution context scope that gets us at least a target and + /// process. + /// + /// @param[in] function_ptr + /// The default function to be called. Can be overridden using + /// WriteFunctionArguments(). + /// + /// @param[in] ast_context + /// The AST context to evaluate argument types in. + /// + /// @param[in] arg_value_list + /// The default values to use when calling this function. Can + /// be overridden using WriteFunctionArguments(). + //------------------------------------------------------------------ + ClangFunction (ExecutionContextScope &exe_scope, + Function &function_ptr, + ClangASTContext *ast_context, + const ValueList &arg_value_list); + + //------------------------------------------------------------------ + /// 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(). + //------------------------------------------------------------------ + ClangFunction (ExecutionContextScope &exe_scope, + const ClangASTType &return_type, + const Address& function_address, + const ValueList &arg_value_list); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~ClangFunction(); + + //------------------------------------------------------------------ + /// Compile the wrapper function + /// + /// @param[in] errors + /// The stream to print parser errors to. + /// + /// @return + /// The number of errors. + //------------------------------------------------------------------ + unsigned + CompileFunction (Stream &errors); + + //------------------------------------------------------------------ + /// Insert the default function wrapper and its default argument struct + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in,out] args_addr_ref + /// The address of the structure to write the arguments into. May + /// be LLDB_INVALID_ADDRESS; if it is, a new structure is allocated + /// and args_addr_ref is pointed to it. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool + InsertFunction (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Stream &errors); + + //------------------------------------------------------------------ + /// Insert the default function wrapper (using the JIT) + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool WriteFunctionWrapper (ExecutionContext &exe_ctx, + Stream &errors); + + //------------------------------------------------------------------ + /// Insert the default function argument struct + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in,out] args_addr_ref + /// The address of the structure to write the arguments into. May + /// be LLDB_INVALID_ADDRESS; if it is, a new structure is allocated + /// and args_addr_ref is pointed to it. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Stream &errors); + + //------------------------------------------------------------------ + /// Insert an argument struct with a non-default function address and + /// non-default argument values + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in,out] args_addr_ref + /// The address of the structure to write the arguments into. May + /// be LLDB_INVALID_ADDRESS; if it is, a new structure is allocated + /// and args_addr_ref is pointed to it. + /// + /// @param[in] function_address + /// The address of the function to call. + /// + /// @param[in] arg_values + /// The values of the function's arguments. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Address function_address, + ValueList &arg_values, + Stream &errors); + + //------------------------------------------------------------------ + /// [Static] Execute a function, passing it a single void* parameter. + /// ClangFunction uses this to call the wrapper function. + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] function_address + /// The address of the function in the target process. + /// + /// @param[in] void_arg + /// The value of the void* parameter. + /// + /// @param[in] stop_others + /// True if other threads should pause during execution. + /// + /// @param[in] try_all_threads + /// If the timeout expires, true if other threads should run. If + /// the function may try to take locks, this is useful. + /// + /// @param[in] unwind_on_error + /// If true, and the execution stops before completion, we unwind the + /// function call, and return the program state to what it was before the + /// execution. If false, we leave the program in the stopped state. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @param[in] this_arg + /// If non-NULL, the function is invoked like a C++ method, with the + /// value pointed to by the pointer as its 'this' argument. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + static ExecutionResults + ExecuteFunction (ExecutionContext &exe_ctx, + lldb::addr_t function_address, + lldb::addr_t &void_arg, + bool stop_others, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors, + lldb::addr_t* this_arg = 0); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This simple version will run the function stopping other threads + /// for a fixed timeout period (1000 usec) and if it does not complete, + /// we halt the process and try with all threads running. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + Stream &errors, + Value &results); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This simple version will run the function obeying the stop_others + /// argument. There is no timeout. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] stop_others + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + Stream &errors, bool stop_others, + Value &results); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This simple version will run the function on one thread. If \a timeout_usec + /// is not zero, we time out after that timeout. If \a try_all_threads is true, then we will + /// resume with all threads on, otherwise we halt the process, and eExecutionInterrupted will be returned. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// @param[in] try_all_threads + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + Stream &errors, + uint32_t single_thread_timeout_usec, + bool try_all_threads, + Value &results); + + //------------------------------------------------------------------ + /// Run the function this ClangFunction was created with. + /// + /// This is the full version. + /// + /// @param[in] exe_ctx + /// The thread & process in which this function will run. + /// + /// @param[in] args_addr_ptr + /// If NULL, the function will take care of allocating & deallocating the wrapper + /// args structure. Otherwise, if set to LLDB_INVALID_ADDRESS, a new structure + /// will be allocated, filled and the address returned to you. You are responsible + /// for deallocating it. And if passed in with a value other than LLDB_INVALID_ADDRESS, + /// this should point to an already allocated structure with the values already written. + /// + /// @param[in] errors + /// Errors will be written here if there are any. + /// + /// @param[in] stop_others + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// + /// @param[in] try_all_threads + /// If \b true, run only this thread, if \b false let all threads run. + /// + /// @param[out] results + /// The result value will be put here after running the function. + /// + /// @return + /// Returns one of the ExecutionResults enum indicating function call status. + //------------------------------------------------------------------ + ExecutionResults + ExecuteFunction(ExecutionContext &exe_ctx, + lldb::addr_t *args_addr_ptr, + Stream &errors, + bool stop_others, + uint32_t timeout_usec, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + Value &results); + + //------------------------------------------------------------------ + /// [static] Get a thread plan to run a function. + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] func_addr + /// The address of the function in the target process. + /// + /// @param[in] args_addr_ref + /// The value of the void* parameter. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @param[in] stop_others + /// True if other threads should pause during execution. + /// + /// @param[in] unwind_on_error + /// True if the thread plan may simply be discarded if an error occurs. + /// + /// @param[in] ignore_breakpoints + /// True if the expression execution will ignore breakpoint hits and continue executing. + /// + /// @param[in] this_arg + /// If non-NULL (and cmd_arg is NULL), the function is invoked like a C++ + /// method, with the value pointed to by the pointer as its 'this' + /// argument. + /// + /// @param[in] cmd_arg + /// If non-NULL, the function is invoked like an Objective-C method, with + /// this_arg in the 'self' slot and cmd_arg in the '_cmd' slot + /// + /// @return + /// A ThreadPlan for executing the function. + //------------------------------------------------------------------ + static ThreadPlan * + GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t func_addr, + lldb::addr_t &args_addr_ref, + Stream &errors, + bool stop_others, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg = 0, + lldb::addr_t *cmd_arg = 0); + + //------------------------------------------------------------------ + /// Get a thread plan to run the function this ClangFunction was created with. + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] func_addr + /// The address of the function in the target process. + /// + /// @param[in] args_addr_ref + /// The value of the void* parameter. + /// + /// @param[in] errors + /// The stream to write errors to. + /// + /// @param[in] stop_others + /// True if other threads should pause during execution. + /// + /// @param[in] unwind_on_error + /// True if the thread plan may simply be discarded if an error occurs. + /// + /// @return + /// A ThreadPlan for executing the function. + //------------------------------------------------------------------ + ThreadPlan * + GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Stream &errors, + bool stop_others, + bool unwind_on_error = true, + bool ignore_breakpoints = true) + { + return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + m_jit_start_addr, + args_addr_ref, + errors, + stop_others, + unwind_on_error, + ignore_breakpoints); + } + + //------------------------------------------------------------------ + /// Get the result of the function from its struct + /// + /// @param[in] exe_ctx + /// The execution context to retrieve the result from. + /// + /// @param[in] args_addr + /// The address of the argument struct. + /// + /// @param[in] ret_value + /// The value returned by the function. + /// + /// @return + /// True on success; false otherwise. + //------------------------------------------------------------------ + bool FetchFunctionResults (ExecutionContext &exe_ctx, + lldb::addr_t args_addr, + Value &ret_value); + + //------------------------------------------------------------------ + /// Deallocate the arguments structure + /// + /// @param[in] exe_ctx + /// The execution context to insert the function and its arguments + /// into. + /// + /// @param[in] args_addr + /// The address of the argument struct. + //------------------------------------------------------------------ + void DeallocateFunctionResults (ExecutionContext &exe_ctx, + lldb::addr_t args_addr); + + //------------------------------------------------------------------ + /// Interface for ClangExpression + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + const char * + Text () + { + return m_wrapper_function_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + const char * + FunctionName () + { + return m_wrapper_function_name.c_str(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + ClangExpressionDeclMap * + DeclMap () + { + return NULL; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when registering + /// local variables. May be NULL if the Expression doesn't care. + //------------------------------------------------------------------ + ClangExpressionVariableList * + LocalVariables () + { + return NULL; + } + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + bool + NeedsValidation () + { + return false; + } + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + bool + NeedsVariableResolution () + { + return false; + } + + ValueList + GetArgumentValues () const + { + return m_arg_values; + } +private: + //------------------------------------------------------------------ + // For ClangFunction only + //------------------------------------------------------------------ + + std::unique_ptr m_parser; ///< The parser responsible for compiling the function. + std::unique_ptr m_execution_unit_ap; + + Function *m_function_ptr; ///< The function we're going to call. May be NULL if we don't have debug info for the function. + Address m_function_addr; ///< If we don't have the FunctionSP, we at least need the address & return type. + ClangASTType m_function_return_type; ///< The opaque clang qual type for the function return type. + ClangASTContext *m_clang_ast_context; ///< This is the clang_ast_context that we're getting types from the and value, and the function return the function pointer is NULL. + + std::string m_wrapper_function_name; ///< The name of the wrapper function. + std::string m_wrapper_function_text; ///< The contents of the wrapper function. + std::string m_wrapper_struct_name; ///< The name of the struct that contains the target function address, arguments, and result. + std::list m_wrapper_args_addrs; ///< The addresses of the arguments to the wrapper function. + + bool m_struct_valid; ///< True if the ASTStructExtractor has populated the variables below. + + //------------------------------------------------------------------ + /// These values are populated by the ASTStructExtractor + size_t m_struct_size; ///< The size of the argument struct, in bytes. + std::vector m_member_offsets; ///< The offset of each member in the struct, in bytes. + uint64_t m_return_size; ///< The size of the result variable, in bytes. + uint64_t m_return_offset; ///< The offset of the result variable in the struct, in bytes. + //------------------------------------------------------------------ + + ValueList m_arg_values; ///< The default values of the arguments. + + bool m_compiled; ///< True if the wrapper function has already been parsed. + bool m_JITted; ///< True if the wrapper function has already been JIT-compiled. +}; + +} // Namespace lldb_private + +#endif // lldb_ClangFunction_h_ diff --git a/include/lldb/Expression/ClangPersistentVariables.h b/include/lldb/Expression/ClangPersistentVariables.h new file mode 100644 index 00000000000..6d9dae95473 --- /dev/null +++ b/include/lldb/Expression/ClangPersistentVariables.h @@ -0,0 +1,75 @@ +//===-- ClangPersistentVariables.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangPersistentVariables_h_ +#define liblldb_ClangPersistentVariables_h_ + +#include "lldb/Expression/ClangExpressionVariable.h" +#include "llvm/ADT/DenseMap.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class ClangPersistentVariables ClangPersistentVariables.h "lldb/Expression/ClangPersistentVariables.h" +/// @brief 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 ClangExpressionVariableList +{ +public: + + //---------------------------------------------------------------------- + /// Constructor + //---------------------------------------------------------------------- + ClangPersistentVariables (); + + lldb::ClangExpressionVariableSP + CreatePersistentVariable (const lldb::ValueObjectSP &valobj_sp); + + lldb::ClangExpressionVariableSP + CreatePersistentVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size); + + //---------------------------------------------------------------------- + /// Return the next entry in the sequence of strings "$0", "$1", ... for + /// use naming persistent expression convenience variables. + /// + /// @return + /// A string that contains the next persistent variable name. + //---------------------------------------------------------------------- + ConstString + GetNextPersistentVariableName (); + + void + RemovePersistentVariable (lldb::ClangExpressionVariableSP variable); + + void + RegisterPersistentType (const ConstString &name, + clang::TypeDecl *tag_decl); + + clang::TypeDecl * + GetPersistentType (const ConstString &name); + +private: + uint32_t m_next_persistent_variable_id; ///< The counter used by GetNextResultName(). + + typedef llvm::DenseMap PersistentTypeMap; + PersistentTypeMap m_persistent_types; ///< The persistent types declared by the user. +}; + +} + +#endif diff --git a/include/lldb/Expression/ClangUserExpression.h b/include/lldb/Expression/ClangUserExpression.h new file mode 100644 index 00000000000..47bfebb4664 --- /dev/null +++ b/include/lldb/Expression/ClangUserExpression.h @@ -0,0 +1,432 @@ +//===-- ClangUserExpression.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangUserExpression_h_ +#define liblldb_ClangUserExpression_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/IRForTarget.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Target/ExecutionContext.h" + +#include "llvm/ExecutionEngine/JITMemoryManager.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class ClangUserExpression ClangUserExpression.h "lldb/Expression/ClangUserExpression.h" +/// @brief 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 ClangExpression +{ +public: + typedef std::shared_ptr ClangUserExpressionSP; + + enum { kDefaultTimeout = 500000u }; + //------------------------------------------------------------------ + /// 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. + //------------------------------------------------------------------ + ClangUserExpression (const char *expr, + const char *expr_prefix, + lldb::LanguageType language, + ResultType desired_type); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~ClangUserExpression (); + + //------------------------------------------------------------------ + /// Parse the expression + /// + /// @param[in] error_stream + /// A stream to print 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 (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory); + + bool + CanInterpret () + { + return m_can_interpret; + } + + bool + MatchesContext (ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Execute the parsed expression + /// + /// @param[in] error_stream + /// A stream to print errors to. + /// + /// @param[in] exe_ctx + /// The execution context to use when looking up entities that + /// are needed for parsing (locations of variables, etc.) + /// + /// @param[in] unwind_on_error + /// If true, and the execution stops before completion, we unwind the + /// function call, and return the program state to what it was before the + /// execution. If false, we leave the program in the stopped state. + /// + /// @param[in] ignore_breakpoints + /// If true, ignore breakpoints while executing the expression. + /// + /// @param[in] shared_ptr_to_me + /// This is a shared pointer to this ClangUserExpression. This is + /// needed because Execute can push a thread plan that will hold onto + /// the ClangUserExpression for an unbounded period of time. So you + /// need to give the thread plan a reference to this object that can + /// keep it alive. + /// + /// @param[in] result + /// A pointer to direct at the persistent variable in which the + /// expression's result is stored. + /// + /// @param[in] try_all_threads + /// If true, then we will try to run all threads if the function doesn't complete on + /// one thread. See timeout_usec for the interaction of this variable and + /// the timeout. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// + /// @return + /// A Process::Execution results value. + //------------------------------------------------------------------ + ExecutionResults + Execute (Stream &error_stream, + ExecutionContext &exe_ctx, + bool unwind_on_error, + bool ignore_breakpoints, + ClangUserExpressionSP &shared_ptr_to_me, + lldb::ClangExpressionVariableSP &result, + bool try_all_threads, + uint32_t timeout_usec); + + ThreadPlan * + GetThreadPlanToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Apply the side effects of the function to program state. + /// + /// @param[in] error_stream + /// A stream to print errors to. + /// + /// @param[in] exe_ctx + /// The execution context to use when looking up entities that + /// are needed for parsing (locations of variables, etc.) + /// + /// @param[in] result + /// A pointer to direct at the persistent variable in which the + /// expression's result is stored. + /// + /// @param[in] function_stack_pointer + /// A pointer to the base of the function's stack frame. This + /// is used to determine whether the expession result resides in + /// memory that will still be valid, or whether it needs to be + /// treated as homeless for the purpose of future expressions. + /// + /// @return + /// A Process::Execution results value. + //------------------------------------------------------------------ + bool + FinalizeJITExecution (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::ClangExpressionVariableSP &result, + lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS, + lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS); + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + const char * + Text () + { + return m_transformed_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the string that the user typed. + //------------------------------------------------------------------ + const char * + GetUserText () + { + return m_expr_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + const char * + FunctionName () + { + return "$__lldb_expr"; + } + + //------------------------------------------------------------------ + /// Return the language that should be used when parsing. To use + /// the default, return eLanguageTypeUnknown. + //------------------------------------------------------------------ + virtual lldb::LanguageType + Language () + { + return m_language; + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + ClangExpressionDeclMap * + DeclMap () + { + return m_expr_decl_map.get(); + } + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Return the desired result type of the function, or + /// eResultTypeAny if indifferent. + //------------------------------------------------------------------ + virtual ResultType + DesiredResultType () + { + return m_desired_type; + } + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + bool + NeedsValidation () + { + return true; + } + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + bool + NeedsVariableResolution () + { + return true; + } + + //------------------------------------------------------------------ + /// Evaluate one expression and return its result. + /// + /// @param[in] exe_ctx + /// The execution context to use when evaluating the expression. + /// + /// @param[in] execution_policy + /// Determines whether or not to try using the IR interpreter to + /// avoid running the expression on the parser. + /// + /// @param[in] language + /// If not eLanguageTypeUnknown, a language to use when parsing + /// the expression. Currently restricted to those languages + /// supported by Clang. + /// + /// @param[in] unwind_on_error + /// True if the thread's state should be restored in the case + /// of an error. + /// + /// @param[in] ignore_breakpoints + /// If true, ignore breakpoints while executing the expression. + /// + /// @param[in] result_type + /// If not eResultTypeAny, the type of the desired result. Will + /// result in parse errors if impossible. + /// + /// @param[in] expr_cstr + /// A C string containing the expression to be evaluated. + /// + /// @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/out] result_valobj_sp + /// If execution is successful, the result valobj is placed here. + /// + /// @param[in] try_all_threads + /// If true, then we will try to run all threads if the function doesn't complete on + /// one thread. See timeout_usec for the interaction of this variable and + /// the timeout. + /// + /// @param[in] timeout_usec + /// Timeout value (0 for no timeout). If try_all_threads is true, then we + /// will try on one thread for the lesser of .25 sec and half the total timeout. + /// then switch to running all threads, otherwise this will be the total timeout. + /// + /// @result + /// A Process::ExecutionResults value. eExecutionCompleted for success. + //------------------------------------------------------------------ + static ExecutionResults + Evaluate (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + bool try_all_threads, + uint32_t timeout_usec); + + static ExecutionResults + EvaluateWithError (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + Error &error, + bool try_all_threads, + uint32_t timeout_usec); + + static const Error::ValueType kNoResult = 0x1001; ///< ValueObject::GetError() returns this if there is no result from the expression. +private: + //------------------------------------------------------------------ + /// Populate m_cplusplus and m_objetivec based on the environment. + //------------------------------------------------------------------ + + void + ScanContext (ExecutionContext &exe_ctx, + lldb_private::Error &err); + + bool + PrepareToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::addr_t &struct_address, + lldb::addr_t &object_ptr, + lldb::addr_t &cmd_ptr); + + void + InstallContext (ExecutionContext &exe_ctx); + + bool + LockAndCheckContext (ExecutionContext &exe_ctx, + lldb::TargetSP &target_sp, + lldb::ProcessSP &process_sp, + lldb::StackFrameSP &frame_sp); + + lldb::ProcessWP m_process_wp; ///< The process used as the context for the expression. + Address m_address; ///< The address the process is stopped in. + lldb::addr_t m_stack_frame_bottom; ///< The bottom of the allocated stack frame. + lldb::addr_t m_stack_frame_top; ///< The top of the allocated stack frame. + + std::string m_expr_text; ///< The text of the expression, as typed by the user + std::string m_expr_prefix; ///< The text of the translation-level definitions, as provided by the user + lldb::LanguageType m_language; ///< The language to use when parsing (eLanguageTypeUnknown means use defaults) + bool m_allow_cxx; ///< True if the language allows C++. + bool m_allow_objc; ///< True if the language allows Objective-C. + std::string m_transformed_text; ///< The text of the expression, as send to the parser + ResultType m_desired_type; ///< The type to coerce the expression's result to. If eResultTypeAny, inferred from the expression. + + std::unique_ptr m_expr_decl_map; ///< The map to use when parsing the expression. + std::unique_ptr m_execution_unit_ap; ///< The execution unit the expression is stored in. + std::unique_ptr m_materializer_ap; ///< The materializer to use when running the expression. + std::unique_ptr m_result_synthesizer; ///< The result synthesizer, if one is needed. + + bool m_enforce_valid_object; ///< True if the expression parser should enforce the presence of a valid class pointer in order to generate the expression as a method. + bool m_cplusplus; ///< True if the expression is compiled as a C++ member function (true if it was parsed when exe_ctx was in a C++ method). + bool m_objectivec; ///< True if the expression is compiled as an Objective-C method (true if it was parsed when exe_ctx was in an Objective-C method). + bool m_static_method; ///< True if the expression is compiled as a static (or class) method (currently true if it was parsed when exe_ctx was in an Objective-C class method). + bool m_needs_object_ptr; ///< True if "this" or "self" must be looked up and passed in. False if the expression doesn't really use them and they can be NULL. + bool m_const_object; ///< True if "this" is const. + Target *m_target; ///< The target for storing persistent data like types and variables. + + bool m_can_interpret; ///< True if the expression could be evaluated statically; false otherwise. + lldb::addr_t m_materialized_address; ///< The address at which the arguments to the expression have been materialized. + Materializer::DematerializerSP m_dematerializer_sp; ///< The dematerializer. +}; + +} // namespace lldb_private + +#endif // liblldb_ClangUserExpression_h_ diff --git a/include/lldb/Expression/ClangUtilityFunction.h b/include/lldb/Expression/ClangUtilityFunction.h new file mode 100644 index 00000000000..6da8e5ec3a8 --- /dev/null +++ b/include/lldb/Expression/ClangUtilityFunction.h @@ -0,0 +1,179 @@ +//===-- ClangUtilityFunction.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangUtilityFunction_h_ +#define liblldb_ClangUtilityFunction_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Expression/ClangExpression.h" + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class ClangUtilityFunction ClangUtilityFunction.h "lldb/Expression/ClangUtilityFunction.h" +/// @brief 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, +//---------------------------------------------------------------------- +class ClangUtilityFunction : public ClangExpression +{ +public: + //------------------------------------------------------------------ + /// 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 (const char *text, + const char *name); + + virtual + ~ClangUtilityFunction (); + + //------------------------------------------------------------------ + /// Install the utility function into a process + /// + /// @param[in] error_stream + /// A stream to print parse 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 + Install (Stream &error_stream, ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Check whether the given PC is inside the function + /// + /// Especially useful if the function dereferences NULL to indicate a failed + /// assert. + /// + /// @param[in] pc + /// The program counter to check. + /// + /// @return + /// True if the program counter falls within the function's bounds; + /// false if not (or the function is not JIT compiled) + //------------------------------------------------------------------ + bool + ContainsAddress (lldb::addr_t address) + { + // nothing is both >= LLDB_INVALID_ADDRESS and < LLDB_INVALID_ADDRESS, + // so this always returns false if the function is not JIT compiled yet + return (address >= m_jit_start_addr && address < m_jit_end_addr); + } + + + //------------------------------------------------------------------ + /// Return the string that the parser should parse. Must be a full + /// translation unit. + //------------------------------------------------------------------ + const char * + Text () + { + return m_function_text.c_str(); + } + + //------------------------------------------------------------------ + /// Return the function name that should be used for executing the + /// expression. Text() should contain the definition of this + /// function. + //------------------------------------------------------------------ + const char * + FunctionName () + { + return m_function_name.c_str(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when resolving external + /// values. May be NULL if everything should be self-contained. + //------------------------------------------------------------------ + ClangExpressionDeclMap * + DeclMap () + { + return m_expr_decl_map.get(); + } + + //------------------------------------------------------------------ + /// Return the object that the parser should use when registering + /// local variables. May be NULL if the Expression doesn't care. + //------------------------------------------------------------------ + ClangExpressionVariableList * + LocalVariables () + { + return NULL; + } + + //------------------------------------------------------------------ + /// 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) + { + return NULL; + } + + //------------------------------------------------------------------ + /// Return true if validation code should be inserted into the + /// expression. + //------------------------------------------------------------------ + bool + NeedsValidation () + { + return false; + } + + //------------------------------------------------------------------ + /// Return true if external variables in the expression should be + /// resolved. + //------------------------------------------------------------------ + bool + NeedsVariableResolution () + { + return false; + } + +private: + std::unique_ptr m_expr_decl_map; ///< The map to use when parsing and materializing the expression. + std::unique_ptr m_execution_unit_ap; + + std::string m_function_text; ///< The text of the function. Must be a well-formed translation unit. + std::string m_function_name; ///< The name of the function. +}; + +} // namespace lldb_private + +#endif // liblldb_ClangUtilityFunction_h_ diff --git a/include/lldb/Expression/DWARFExpression.h b/include/lldb/Expression/DWARFExpression.h new file mode 100644 index 00000000000..2692831ecc8 --- /dev/null +++ b/include/lldb/Expression/DWARFExpression.h @@ -0,0 +1,424 @@ +//===-- DWARFExpression.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFExpression_h_ +#define liblldb_DWARFExpression_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" + +namespace lldb_private { + +class ClangExpressionVariable; +class ClangExpressionVariableList; + +class ClangExpressionDeclMap; + +//---------------------------------------------------------------------- +/// @class DWARFExpression DWARFExpression.h "lldb/Expression/DWARFExpression.h" +/// @brief Encapsulates a DWARF location expression and interprets it. +/// +/// DWARF location expressions are used in two ways by LLDB. The first +/// use is to find entities specified in the debug information, since +/// their locations are specified in precisely this language. The second +/// is to interpret expressions without having to run the target in cases +/// where the overhead from copying JIT-compiled code into the target is +/// too high or where the target cannot be run. This class encapsulates +/// a single DWARF location expression or a location list and interprets +/// it. +//---------------------------------------------------------------------- +class DWARFExpression +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + DWARFExpression(); + + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] data + /// A data extractor configured to read the DWARF location expression's + /// bytecode. + /// + /// @param[in] data_offset + /// The offset of the location expression in the extractor. + /// + /// @param[in] data_length + /// The byte length of the location expression. + //------------------------------------------------------------------ + DWARFExpression(const DataExtractor& data, + lldb::offset_t data_offset, + lldb::offset_t data_length); + + //------------------------------------------------------------------ + /// Copy constructor + //------------------------------------------------------------------ + DWARFExpression(const DWARFExpression& rhs); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~DWARFExpression(); + + //------------------------------------------------------------------ + /// Print the description of the expression to a stream + /// + /// @param[in] s + /// The stream to print to. + /// + /// @param[in] level + /// The level of verbosity to use. + /// + /// @param[in] location_list_base_addr + /// If this is a location list based expression, this is the + /// address of the object that owns it. NOTE: this value is + /// different from the DWARF version of the location list base + /// address which is compile unit relative. This base address + /// is the address of the object that owns the location list. + /// + /// @param[in] abi + /// An optional ABI plug-in that can be used to resolve register + /// names. + //------------------------------------------------------------------ + void + GetDescription (Stream *s, + lldb::DescriptionLevel level, + lldb::addr_t location_list_base_addr, + ABI *abi) const; + + //------------------------------------------------------------------ + /// Return true if the location expression contains data + //------------------------------------------------------------------ + bool + IsValid() const; + + //------------------------------------------------------------------ + /// Return true if a location list was provided + //------------------------------------------------------------------ + bool + IsLocationList() const; + + //------------------------------------------------------------------ + /// Search for a load address in the location list + /// + /// @param[in] process + /// The process to use when resolving the load address + /// + /// @param[in] addr + /// The address to resolve + /// + /// @return + /// True if IsLocationList() is true and the address was found; + /// false otherwise. + //------------------------------------------------------------------ +// bool +// LocationListContainsLoadAddress (Process* process, const Address &addr) const; +// + bool + LocationListContainsAddress (lldb::addr_t loclist_base_addr, lldb::addr_t addr) const; + + //------------------------------------------------------------------ + /// If a location is not a location list, return true if the location + /// contains a DW_OP_addr () opcode in the stream that matches \a + /// file_addr. If file_addr is LLDB_INVALID_ADDRESS, the this + /// function will return true if the variable there is any DW_OP_addr + /// in a location that (yet still is NOT a location list). This helps + /// us detect if a variable is a global or static variable since + /// there is no other indication from DWARF debug info. + /// + /// @param[in] op_addr_idx + /// The DW_OP_addr index to retrieve in case there is more than + /// one DW_OP_addr opcode in the location byte stream. + /// + /// @param[out] error + /// If the location stream contains unknown DW_OP opcodes or the + /// data is missing, \a error will be set to \b true. + /// + /// @return + /// LLDB_INVALID_ADDRESS if the location doesn't contain a + /// DW_OP_addr for \a op_addr_idx, otherwise a valid file address + //------------------------------------------------------------------ + lldb::addr_t + GetLocation_DW_OP_addr (uint32_t op_addr_idx, bool &error) const; + + bool + Update_DW_OP_addr (lldb::addr_t file_addr); + + //------------------------------------------------------------------ + /// Make the expression parser read its location information from a + /// given data source. Does not change the offset and length + /// + /// @param[in] data + /// A data extractor configured to read the DWARF location expression's + /// bytecode. + //------------------------------------------------------------------ + void + SetOpcodeData(const DataExtractor& data); + + //------------------------------------------------------------------ + /// Make the expression parser read its location information from a + /// given data source + /// + /// @param[in] data + /// A data extractor configured to read the DWARF location expression's + /// bytecode. + /// + /// @param[in] data_offset + /// The offset of the location expression in the extractor. + /// + /// @param[in] data_length + /// The byte length of the location expression. + //------------------------------------------------------------------ + void + SetOpcodeData(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length); + + //------------------------------------------------------------------ + /// Copy the DWARF location expression into a local buffer. + /// + /// It is a good idea to copy the data so we don't keep the entire + /// object file worth of data around just for a few bytes of location + /// expression. LLDB typically will mmap the entire contents of debug + /// information files, and if we use SetOpcodeData, it will get a + /// shared reference to all of this data for the and cause the object + /// file to have to stay around. Even worse, a very very large ".a" + /// that contains one or more .o files could end up being referenced. + /// Location lists are typically small so even though we are copying + /// the data, it shouldn't amount to that much for the variables we + /// end up parsing. + /// + /// @param[in] data + /// A data extractor configured to read and copy the DWARF + /// location expression's bytecode. + /// + /// @param[in] data_offset + /// The offset of the location expression in the extractor. + /// + /// @param[in] data_length + /// The byte length of the location expression. + //------------------------------------------------------------------ + void + CopyOpcodeData (const DataExtractor& data, + lldb::offset_t data_offset, + lldb::offset_t data_length); + + + //------------------------------------------------------------------ + /// Tells the expression that it refers to a location list. + /// + /// @param[in] slide + /// This value should be a slide that is applied to any values + /// in the location list data so the values become zero based + /// offsets into the object that owns the location list. We need + /// to make location lists relative to the objects that own them + /// so we can relink addresses on the fly. + //------------------------------------------------------------------ + void + SetLocationListSlide (lldb::addr_t slide); + + //------------------------------------------------------------------ + /// Return the call-frame-info style register kind + //------------------------------------------------------------------ + int + GetRegisterKind (); + + //------------------------------------------------------------------ + /// Set the call-frame-info style register kind + /// + /// @param[in] reg_kind + /// The register kind. + //------------------------------------------------------------------ + void + SetRegisterKind (lldb::RegisterKind reg_kind); + + //------------------------------------------------------------------ + /// Wrapper for the static evaluate function that accepts an + /// ExecutionContextScope instead of an ExecutionContext and uses + /// member variables to populate many operands + //------------------------------------------------------------------ + bool + Evaluate (ExecutionContextScope *exe_scope, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr) const; + + //------------------------------------------------------------------ + /// Wrapper for the static evaluate function that uses member + /// variables to populate many operands + //------------------------------------------------------------------ + bool + Evaluate (ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr) const; + + //------------------------------------------------------------------ + /// Evaluate a DWARF location expression in a particular context + /// + /// @param[in] exe_ctx + /// The execution context in which to evaluate the location + /// expression. The location expression may access the target's + /// memory, especially if it comes from the expression parser. + /// + /// @param[in] opcodes + /// This is a static method so the opcodes need to be provided + /// explicitly. + /// + /// @param[in] expr_locals + /// If the location expression was produced by the expression parser, + /// the list of local variables referenced by the DWARF expression. + /// This list should already have been populated during parsing; + /// the DWARF expression refers to variables by index. Can be NULL if + /// the location expression uses no locals. + /// + /// @param[in] decl_map + /// If the location expression was produced by the expression parser, + /// the list of external variables referenced by the location + /// expression. Can be NULL if the location expression uses no + /// external variables. + /// + /// @param[in] reg_ctx + /// An optional parameter which provides a RegisterContext for use + /// when evaluating the expression (i.e. for fetching register values). + /// Normally this will come from the ExecutionContext's StackFrame but + /// in the case where an expression needs to be evaluated while building + /// the stack frame list, this short-cut is available. + /// + /// @param[in] offset + /// The offset of the location expression in the data extractor. + /// + /// @param[in] length + /// The length in bytes of the location expression. + /// + /// @param[in] reg_set + /// The call-frame-info style register kind. + /// + /// @param[in] initial_value_ptr + /// A value to put on top of the interpreter stack before evaluating + /// the expression, if the expression is parametrized. Can be NULL. + /// + /// @param[in] result + /// A value into which the result of evaluating the expression is + /// to be placed. + /// + /// @param[in] error_ptr + /// If non-NULL, used to report errors in expression evaluation. + /// + /// @return + /// True on success; false otherwise. If error_ptr is non-NULL, + /// details of the failure are provided through it. + //------------------------------------------------------------------ + static bool + Evaluate (ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + const DataExtractor& opcodes, + const lldb::offset_t offset, + const lldb::offset_t length, + const uint32_t reg_set, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr); + + //------------------------------------------------------------------ + /// Loads a ClangExpressionVariableList into the object + /// + /// @param[in] locals + /// If non-NULL, the list of locals used by this expression. + /// See Evaluate(). + //------------------------------------------------------------------ + void + SetExpressionLocalVariableList (ClangExpressionVariableList *locals); + + //------------------------------------------------------------------ + /// Loads a ClangExpressionDeclMap into the object + /// + /// @param[in] locals + /// If non-NULL, the list of external variables used by this + /// expression. See Evaluate(). + //------------------------------------------------------------------ + void + SetExpressionDeclMap (ClangExpressionDeclMap *decl_map); + + bool + GetExpressionData (DataExtractor &data) const + { + data = m_data; + return data.GetByteSize() > 0; + } + + bool + DumpLocationForAddress (Stream *s, + lldb::DescriptionLevel level, + lldb::addr_t loclist_base_load_addr, + lldb::addr_t address, + ABI *abi); + +protected: + //------------------------------------------------------------------ + /// Pretty-prints the location expression to a stream + /// + /// @param[in] stream + /// The stream to use for pretty-printing. + /// + /// @param[in] offset + /// The offset into the data buffer of the opcodes to be printed. + /// + /// @param[in] length + /// The length in bytes of the opcodes to be printed. + /// + /// @param[in] level + /// The level of detail to use in pretty-printing. + /// + /// @param[in] abi + /// An optional ABI plug-in that can be used to resolve register + /// names. + //------------------------------------------------------------------ + void + DumpLocation(Stream *s, + lldb::offset_t offset, + lldb::offset_t length, + lldb::DescriptionLevel level, + ABI *abi) const; + + bool + GetLocation (lldb::addr_t base_addr, + lldb::addr_t pc, + lldb::offset_t &offset, + lldb::offset_t &len); + + //------------------------------------------------------------------ + /// Classes that inherit from DWARFExpression can see and modify these + //------------------------------------------------------------------ + + DataExtractor m_data; ///< A data extractor capable of reading opcode bytes + lldb::RegisterKind m_reg_kind; ///< One of the defines that starts with LLDB_REGKIND_ + lldb::addr_t m_loclist_slide; ///< A value used to slide the location list offsets so that + ///< they are relative to the object that owns the location list + ///< (the function for frame base and variable location lists) + +}; + +} // namespace lldb_private + +#endif // liblldb_DWARFExpression_h_ diff --git a/include/lldb/Expression/ExpressionSourceCode.h b/include/lldb/Expression/ExpressionSourceCode.h new file mode 100644 index 00000000000..be1014ae304 --- /dev/null +++ b/include/lldb/Expression/ExpressionSourceCode.h @@ -0,0 +1,78 @@ +//===-- ExpressionSourceCode.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ExpressionSourceCode_h +#define liblldb_ExpressionSourceCode_h + +#include "lldb/lldb-enumerations.h" + +#include + +namespace lldb_private +{ + +class ExpressionSourceCode +{ +public: + static const char * g_expression_prefix; + + static ExpressionSourceCode *CreateWrapped (const char *prefix, + const char *body) + { + return new ExpressionSourceCode ("$__lldb_expr", + prefix, + body, + true); + } + + static ExpressionSourceCode *CreateUnwrapped (const char *name, + const char *body) + { + return new ExpressionSourceCode (name, + "", + body, + false); + } + + bool NeedsWrapping () const + { + return m_wrap; + } + + const char *GetName () const + { + return m_name.c_str(); + } + + bool GetText (std::string &text, + lldb::LanguageType wrapping_language, + bool const_object, + bool static_method) const; + +private: + ExpressionSourceCode (const char *name, + const char *prefix, + const char *body, + bool wrap) : + m_name(name), + m_prefix(prefix), + m_body(body), + m_wrap(wrap) + { + } + + std::string m_name; + std::string m_prefix; + std::string m_body; + bool m_wrap; +}; + +} // namespace lldb_private + +#endif diff --git a/include/lldb/Expression/IRDynamicChecks.h b/include/lldb/Expression/IRDynamicChecks.h new file mode 100644 index 00000000000..226f5c94e98 --- /dev/null +++ b/include/lldb/Expression/IRDynamicChecks.h @@ -0,0 +1,169 @@ +//===-- IRDynamicChecks.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRDynamicChecks_h_ +#define liblldb_IRDynamicChecks_h_ + +#include "lldb/lldb-types.h" +#include "llvm/Pass.h" + +namespace llvm { + class BasicBlock; + class CallInst; + class Constant; + class Function; + class Instruction; + class Module; + class DataLayout; + class Value; +} + +namespace lldb_private +{ + +class ClangExpressionDeclMap; +class ClangUtilityFunction; +class ExecutionContext; +class Stream; + +//---------------------------------------------------------------------- +/// @class DynamicCheckerFunctions IRDynamicChecks.h "lldb/Expression/IRDynamicChecks.h" +/// @brief Encapsulates dynamic check functions used by expressions. +/// +/// Each of the utility functions encapsulated in this class is responsible +/// for validating some data that an expression is about to use. Examples are: +/// +/// a = *b; // check that b is a valid pointer +/// [b init]; // check that b is a valid object to send "init" to +/// +/// The class installs each checker function into the target process and +/// makes it available to IRDynamicChecks to use. +//---------------------------------------------------------------------- +class DynamicCheckerFunctions +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + DynamicCheckerFunctions (); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~DynamicCheckerFunctions (); + + //------------------------------------------------------------------ + /// Install the utility functions into a process. This binds the + /// instance of DynamicCheckerFunctions to that process. + /// + /// @param[in] error_stream + /// A stream to print errors on. + /// + /// @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 (Stream &error_stream, + ExecutionContext &exe_ctx); + + bool DoCheckersExplainStop (lldb::addr_t addr, Stream &message); + + std::unique_ptr m_valid_pointer_check; + std::unique_ptr m_objc_object_check; +}; + +//---------------------------------------------------------------------- +/// @class IRDynamicChecks IRDynamicChecks.h "lldb/Expression/IRDynamicChecks.h" +/// @brief 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 (DynamicCheckerFunctions &checker_functions, + const char* func_name = "$__lldb_expr"); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~IRDynamicChecks(); + + //------------------------------------------------------------------ + /// 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); + + //------------------------------------------------------------------ + /// Interface stub + //------------------------------------------------------------------ + void assignPassManager(llvm::PMStack &PMS, + llvm::PassManagerType T = llvm::PMT_ModulePassManager); + + //------------------------------------------------------------------ + /// Returns PMT_ModulePassManager + //------------------------------------------------------------------ + llvm::PassManagerType getPotentialPassManagerType() const; +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 + DynamicCheckerFunctions &m_checker_functions; ///< The checker functions for the process +}; + +} + +#endif diff --git a/include/lldb/Expression/IRExecutionUnit.h b/include/lldb/Expression/IRExecutionUnit.h new file mode 100644 index 00000000000..885b6516b0c --- /dev/null +++ b/include/lldb/Expression/IRExecutionUnit.h @@ -0,0 +1,495 @@ +//===-- IRExecutionUnit.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_IRExecutionUnit_h_ +#define lldb_IRExecutionUnit_h_ + +// C Includes +// C++ Includes +#include +#include +#include +#include + +// Other libraries and framework includes +#include "llvm/IR/Module.h" + +// Project includes +#include "lldb/lldb-forward.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/DataBufferHeap.h" +#include "llvm/ExecutionEngine/JITMemoryManager.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Host/Mutex.h" + +namespace llvm { + +class Module; +class ExecutionEngine; + +} + +namespace lldb_private { + +class Error; + +//---------------------------------------------------------------------- +/// @class IRExecutionUnit IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" +/// @brief Contains the IR and, optionally, JIT-compiled code for a module. +/// +/// This class encapsulates the compiled version of an expression, in IR +/// form (for interpretation purposes) and in raw machine code form (for +/// execution in the target). +/// +/// This object wraps an IR module that comes from the expression parser, +/// and knows how to use the JIT to make it into executable code. It can +/// then be used as input to the IR interpreter, or the address of the +/// executable code can be passed to a thread plan to run in the target. +/// +/// This class creates a subclass of LLVM's JITMemoryManager, because that is +/// how the JIT emits code. Because LLDB needs to move JIT-compiled code +/// into the target process, the IRExecutionUnit knows how to copy the +/// emitted code into the target process. +//---------------------------------------------------------------------- +class IRExecutionUnit : public IRMemoryMap +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + IRExecutionUnit (std::unique_ptr &context_ap, + std::unique_ptr &module_ap, + ConstString &name, + const lldb::TargetSP &target_sp, + std::vector &cpu_features); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~IRExecutionUnit(); + + llvm::Module *GetModule() + { + return m_module; + } + + llvm::Function *GetFunction() + { + if (m_module) + return m_module->getFunction (m_name.AsCString()); + else + return NULL; + } + + void GetRunnableInfo(Error &error, + lldb::addr_t &func_addr, + lldb::addr_t &func_end); + + //------------------------------------------------------------------ + /// Accessors for IRForTarget and other clients that may want binary + /// data placed on their behalf. The binary data is owned by the + /// IRExecutionUnit unless the client explicitly chooses to free it. + //------------------------------------------------------------------ + + lldb::addr_t WriteNow(const uint8_t *bytes, + size_t size, + Error &error); + + void FreeNow(lldb::addr_t allocation); + +private: + //------------------------------------------------------------------ + /// Look up the object in m_address_map that contains a given address, + /// find where it was copied to, and return the remote address at the + /// same offset into the copied entity + /// + /// @param[in] local_address + /// The address in the debugger. + /// + /// @return + /// The address in the target process. + //------------------------------------------------------------------ + lldb::addr_t + GetRemoteAddressForLocal (lldb::addr_t local_address); + + //------------------------------------------------------------------ + /// Look up the object in m_address_map that contains a given address, + /// find where it was copied to, and return its address range in the + /// target process + /// + /// @param[in] local_address + /// The address in the debugger. + /// + /// @return + /// The range of the containing object in the target process. + //------------------------------------------------------------------ + typedef std::pair AddrRange; + AddrRange + GetRemoteRangeForLocal (lldb::addr_t local_address); + + //------------------------------------------------------------------ + /// Commit all allocations to the process and record where they were stored. + /// + /// @param[in] process + /// The process to allocate memory in. + /// + /// @return + /// True <=> all allocations were performed successfully. + /// This method will attempt to free allocated memory if the + /// operation fails. + //------------------------------------------------------------------ + bool + CommitAllocations (lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + /// Report all committed allocations to the execution engine. + /// + /// @param[in] engine + /// The execution engine to notify. + //------------------------------------------------------------------ + void + ReportAllocations (llvm::ExecutionEngine &engine); + + //------------------------------------------------------------------ + /// Write the contents of all allocations to the process. + /// + /// @param[in] local_address + /// The process containing the allocations. + /// + /// @return + /// True <=> all allocations were performed successfully. + //------------------------------------------------------------------ + bool + WriteData (lldb::ProcessSP &process_sp); + + Error + DisassembleFunction (Stream &stream, + lldb::ProcessSP &process_sp); + + class MemoryManager : public llvm::JITMemoryManager + { + public: + MemoryManager (IRExecutionUnit &parent); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void setMemoryWritable (); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void setMemoryExecutable (); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void setPoisonMemory (bool poison) + { + m_default_mm_ap->setPoisonMemory (poison); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void AllocateGOT() + { + m_default_mm_ap->AllocateGOT(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual uint8_t *getGOTBase() const + { + return m_default_mm_ap->getGOTBase(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual uint8_t *startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize); + + //------------------------------------------------------------------ + /// Allocate room for a dyld stub for a lazy-referenced function, + /// and add it to the m_stubs map + /// + /// @param[in] F + /// The function being referenced. + /// + /// @param[in] StubSize + /// The size of the stub. + /// + /// @param[in] Alignment + /// The required alignment of the stub. + /// + /// @return + /// Allocated space for the stub. + //------------------------------------------------------------------ + virtual uint8_t *allocateStub(const llvm::GlobalValue* F, + unsigned StubSize, + unsigned Alignment); + + //------------------------------------------------------------------ + /// Complete the body of a function, and add it to the m_functions map + /// + /// @param[in] F + /// The function being completed. + /// + /// @param[in] FunctionStart + /// The first instruction of the function. + /// + /// @param[in] FunctionEnd + /// The last byte of the last instruction of the function. + //------------------------------------------------------------------ + virtual void endFunctionBody(const llvm::Function *F, + uint8_t *FunctionStart, + uint8_t *FunctionEnd); + //------------------------------------------------------------------ + /// Allocate space for an unspecified purpose, and add it to the + /// m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the area. + /// + /// @param[in] Alignment + /// The required alignment of the area. + /// + /// @return + /// Allocated space. + //------------------------------------------------------------------ + virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); + + //------------------------------------------------------------------ + /// Allocate space for executable code, and add it to the + /// m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the area. + /// + /// @param[in] Alignment + /// The required alignment of the area. + /// + /// @param[in] SectionID + /// A unique identifier for the section. + /// + /// @return + /// Allocated space. + //------------------------------------------------------------------ + virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID); + + //------------------------------------------------------------------ + /// Allocate space for data, and add it to the m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the area. + /// + /// @param[in] Alignment + /// The required alignment of the area. + /// + /// @param[in] SectionID + /// A unique identifier for the section. + /// + /// @param[in] IsReadOnly + /// Flag indicating the section is read-only. + /// + /// @return + /// Allocated space. + //------------------------------------------------------------------ + virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, bool IsReadOnly); + + //------------------------------------------------------------------ + /// Allocate space for a global variable, and add it to the + /// m_spaceBlocks map + /// + /// @param[in] Size + /// The size of the variable. + /// + /// @param[in] Alignment + /// The required alignment of the variable. + /// + /// @return + /// Allocated space for the global. + //------------------------------------------------------------------ + virtual uint8_t *allocateGlobal(uintptr_t Size, + unsigned Alignment); + + //------------------------------------------------------------------ + /// Called when object loading is complete and section page + /// permissions can be applied. Currently unimplemented for LLDB. + /// + /// @param[out] ErrMsg + /// The error that prevented the page protection from succeeding. + /// + /// @return + /// True in case of failure, false in case of success. + //------------------------------------------------------------------ + virtual bool finalizeMemory(std::string *ErrMsg) { + // TODO: Ensure that the instruction cache is flushed because + // relocations are updated by dy-load. See: + // sys::Memory::InvalidateInstructionCache + // llvm::SectionMemoryManager + return false; + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void deallocateFunctionBody(void *Body); + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual size_t GetDefaultCodeSlabSize() { + return m_default_mm_ap->GetDefaultCodeSlabSize(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual size_t GetDefaultDataSlabSize() { + return m_default_mm_ap->GetDefaultDataSlabSize(); + } + + virtual size_t GetDefaultStubSlabSize() { + return m_default_mm_ap->GetDefaultStubSlabSize(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual unsigned GetNumCodeSlabs() { + return m_default_mm_ap->GetNumCodeSlabs(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual unsigned GetNumDataSlabs() { + return m_default_mm_ap->GetNumDataSlabs(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual unsigned GetNumStubSlabs() { + return m_default_mm_ap->GetNumStubSlabs(); + } + + //------------------------------------------------------------------ + /// Passthrough interface stub + //------------------------------------------------------------------ + virtual void *getPointerToNamedFunction(const std::string &Name, + bool AbortOnFailure = true) { + return m_default_mm_ap->getPointerToNamedFunction(Name, AbortOnFailure); + } + private: + std::unique_ptr m_default_mm_ap; ///< The memory allocator to use in actually creating space. All calls are passed through to it. + IRExecutionUnit &m_parent; ///< The execution unit this is a proxy for. + }; + + //---------------------------------------------------------------------- + /// @class JittedFunction IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" + /// @brief Encapsulates a single function that has been generated by the JIT. + /// + /// Functions that have been generated by the JIT are first resident in the + /// local process, and then placed in the target process. JittedFunction + /// represents a function possibly resident in both. + //---------------------------------------------------------------------- + struct JittedFunction { + std::string m_name; ///< The function's name + lldb::addr_t m_local_addr; ///< The address of the function in LLDB's memory + lldb::addr_t m_remote_addr; ///< The address of the function in the target's memory + + //------------------------------------------------------------------ + /// Constructor + /// + /// Initializes class variabes. + /// + /// @param[in] name + /// The name of the function. + /// + /// @param[in] local_addr + /// The address of the function in LLDB, or LLDB_INVALID_ADDRESS if + /// it is not present in LLDB's memory. + /// + /// @param[in] remote_addr + /// The address of the function in the target, or LLDB_INVALID_ADDRESS + /// if it is not present in the target's memory. + //------------------------------------------------------------------ + JittedFunction (const char *name, + lldb::addr_t local_addr = LLDB_INVALID_ADDRESS, + lldb::addr_t remote_addr = LLDB_INVALID_ADDRESS) : + m_name (name), + m_local_addr (local_addr), + m_remote_addr (remote_addr) + { + } + }; + + static const unsigned eSectionIDInvalid = (unsigned)-1; + + //---------------------------------------------------------------------- + /// @class AllocationRecord IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" + /// @brief Enacpsulates a single allocation request made by the JIT. + /// + /// Allocations made by the JIT are first queued up and then applied in + /// bulk to the underlying process. + //---------------------------------------------------------------------- + struct AllocationRecord { + lldb::addr_t m_process_address; + uintptr_t m_host_address; + uint32_t m_permissions; + size_t m_size; + unsigned m_alignment; + unsigned m_section_id; + + AllocationRecord (uintptr_t host_address, + uint32_t permissions, + size_t size, + unsigned alignment, + unsigned section_id = eSectionIDInvalid) : + m_process_address(LLDB_INVALID_ADDRESS), + m_host_address(host_address), + m_permissions(permissions), + m_size(size), + m_alignment(alignment), + m_section_id(section_id) + { + } + + void dump (Log *log); + }; + + typedef std::vector RecordVector; + RecordVector m_records; + + std::unique_ptr m_context_ap; + std::unique_ptr m_execution_engine_ap; + std::unique_ptr m_module_ap; ///< Holder for the module until it's been handed off + llvm::Module *m_module; ///< Owned by the execution engine + std::vector m_cpu_features; + llvm::SmallVector m_jitted_functions; ///< A vector of all functions that have been JITted into machine code + const ConstString m_name; + + std::atomic m_did_jit; + + lldb::addr_t m_function_load_addr; + lldb::addr_t m_function_end_load_addr; +}; + +} // namespace lldb_private + +#endif // lldb_IRExecutionUnit_h_ diff --git a/include/lldb/Expression/IRForTarget.h b/include/lldb/Expression/IRForTarget.h new file mode 100644 index 00000000000..151bf2ab477 --- /dev/null +++ b/include/lldb/Expression/IRForTarget.h @@ -0,0 +1,733 @@ +//===-- IRForTarget.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRForTarget_h_ +#define liblldb_IRForTarget_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "llvm/Pass.h" + +#include + +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 Type; + class Value; +} + +namespace lldb_private { + class ClangExpressionDeclMap; + class IRExecutionUnit; + class IRMemoryMap; +} + +//---------------------------------------------------------------------- +/// @class IRForTarget IRForTarget.h "lldb/Expression/IRForTarget.h" +/// @brief 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: + //------------------------------------------------------------------ + /// 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 + //------------------------------------------------------------------ + virtual ~IRForTarget(); + + //------------------------------------------------------------------ + /// 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 + //------------------------------------------------------------------ + virtual bool + runOnModule (llvm::Module &llvm_module); + + //------------------------------------------------------------------ + /// Interface stub + /// + /// Implementation of the llvm::ModulePass::assignPassManager() + /// function. + //------------------------------------------------------------------ + virtual void + assignPassManager (llvm::PMStack &pass_mgr_stack, + llvm::PassManagerType pass_mgr_type = llvm::PMT_ModulePassManager); + + //------------------------------------------------------------------ + /// Returns PMT_ModulePassManager + /// + /// Implementation of the llvm::ModulePass::getPotentialPassManagerType() + /// function. + //------------------------------------------------------------------ + virtual llvm::PassManagerType + getPotentialPassManagerType() const; + +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 fuction, 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. + //------------------------------------------------------------------ + bool + GetFunctionAddress (llvm::Function *function, + uint64_t &ptr, + lldb_private::ConstString &name, + llvm::Constant **&value_ptr); + + //------------------------------------------------------------------ + /// Build a function pointer given a type and a raw pointer. + /// + /// @param[in] type + /// The type of the function pointer to be built. + /// + /// @param[in] ptr + /// The value of the pointer. + /// + /// @return + /// The pointer. + //------------------------------------------------------------------ + llvm::Constant * + BuildFunctionPointer (llvm::Type *type, + uint64_t ptr); + + void + RegisterFunctionMetadata (llvm::LLVMContext &context, + llvm::Value *function_ptr, + const char *name); + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_function + /// The function currently being processed. + /// + /// @return + /// True if the function has side effects (or if this cannot + /// be determined); false otherwise. + //------------------------------------------------------------------ + bool + ResolveFunctionPointers (llvm::Module &llvm_module); + + //------------------------------------------------------------------ + /// 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, + const 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 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 module-level pass to allocate all string literals in a separate + /// allocation and redirect references to them. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ReplaceStrings (); + + //------------------------------------------------------------------ + /// A basick block-level pass to find all literals that will be + /// allocated as statics by the JIT (in contrast to the Strings, + /// which already are statics) and synthesize loads for them. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] basic_block + /// The basic block currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + ReplaceStaticLiterals (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); + + //------------------------------------------------------------------ + /// A module-level pass to remove all global variables from the + /// module since it no longer should export or import any symbols. + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// The top-level pass implementation + /// + /// @param[in] llvm_module + /// The module currently being processed. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool + StripAllGVs (llvm::Module &llvm_module); + + class StaticDataAllocator { + public: + StaticDataAllocator(lldb_private::IRExecutionUnit &execution_unit); + lldb_private::StreamString &GetStream() + { + return m_stream_string; + } + lldb::addr_t Allocate(); + private: + lldb_private::IRExecutionUnit &m_execution_unit; + lldb_private::StreamString m_stream_string; + lldb::addr_t m_allocation; + }; + + /// Flags + bool m_resolve_vars; ///< True if external variable references and persistent variable references should be resolved + std::string 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 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 + StaticDataAllocator m_data_allocator; ///< The allocator to use for constant strings + llvm::Constant *m_CFStringCreateWithBytes; ///< The address of the function CFStringCreateWithBytes, cast to the appropriate function pointer type + llvm::Constant *m_sel_registerName; ///< The address of the function sel_registerName, cast to the appropriate function pointer type + lldb_private::Stream *m_error_stream; ///< If non-NULL, the stream on which errors should be printed + + 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 Maker; + + FunctionValueCache (Maker const &maker); + ~FunctionValueCache (); + llvm::Value *GetValue (llvm::Function *function); + private: + Maker const m_maker; + typedef std::map FunctionValueMap; + FunctionValueMap m_values; + }; + + FunctionValueCache m_entry_instruction_finder; + + static bool + UnfoldConstant (llvm::Constant *old_constant, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder); + + //------------------------------------------------------------------ + /// 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 diff --git a/include/lldb/Expression/IRInterpreter.h b/include/lldb/Expression/IRInterpreter.h new file mode 100644 index 00000000000..5defa8dd202 --- /dev/null +++ b/include/lldb/Expression/IRInterpreter.h @@ -0,0 +1,64 @@ +//===-- IRInterpreter.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRInterpreter_h_ +#define liblldb_IRInterpreter_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Pass.h" + +namespace llvm { + class Function; + class Module; +} + +namespace lldb_private { + +class ClangExpressionDeclMap; +class IRMemoryMap; + +} + +//---------------------------------------------------------------------- +/// @class IRInterpreter IRInterpreter.h "lldb/Expression/IRInterpreter.h" +/// @brief Attempt to interpret the function's code if it does not require +/// running the target. +/// +/// In some cases, the IR for an expression can be evaluated entirely +/// in the debugger, manipulating variables but not executing any code +/// in the target. The IRInterpreter attempts to do this. +//---------------------------------------------------------------------- +class IRInterpreter +{ +public: + static bool + CanInterpret (llvm::Module &module, + llvm::Function &function, + lldb_private::Error &error); + + static bool + Interpret (llvm::Module &module, + llvm::Function &function, + llvm::ArrayRef args, + lldb_private::IRMemoryMap &memory_map, + lldb_private::Error &error, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top); + +private: + static bool + supportsFunction (llvm::Function &llvm_function, + lldb_private::Error &err); +}; + +#endif diff --git a/include/lldb/Expression/IRMemoryMap.h b/include/lldb/Expression/IRMemoryMap.h new file mode 100644 index 00000000000..affe19350e3 --- /dev/null +++ b/include/lldb/Expression/IRMemoryMap.h @@ -0,0 +1,126 @@ +//===-- IRExecutionUnit.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_IRMemoryMap_h_ +#define lldb_IRMemoryMap_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/UserID.h" + +#include + +namespace lldb_private +{ + +//---------------------------------------------------------------------- +/// @class IRMemoryMap IRMemoryMap.h "lldb/Expression/IRMemoryMap.h" +/// @brief Encapsulates memory that may exist in the process but must +/// also be available in the host process. +/// +/// This class encapsulates a group of memory objects that must be readable +/// or writable from the host process regardless of whether the process +/// exists. This allows the IR interpreter as well as JITted code to access +/// the same memory. +/// +/// Point queries against this group of memory objects can be made by the +/// address in the tar at which they reside. If the inferior does not +/// exist, allocations still get made-up addresses. If an inferior appears +/// at some point, then those addresses need to be re-mapped. +//---------------------------------------------------------------------- +class IRMemoryMap +{ +public: + IRMemoryMap (lldb::TargetSP target_sp); + ~IRMemoryMap (); + + enum AllocationPolicy { + eAllocationPolicyInvalid = 0, ///< It is an error for an allocation to have this policy. + eAllocationPolicyHostOnly, ///< This allocation was created in the host and will never make it into the process. + ///< It is an error to create other types of allocations while such allocations exist. + eAllocationPolicyMirror, ///< The intent is that this allocation exist both in the host and the process and have + ///< the same content in both. + eAllocationPolicyProcessOnly ///< The intent is that this allocation exist only in the process. + }; + + lldb::addr_t Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error); + void Leak (lldb::addr_t process_address, Error &error); + void Free (lldb::addr_t process_address, Error &error); + + void WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error); + void WriteScalarToMemory (lldb::addr_t process_address, Scalar &scalar, size_t size, Error &error); + void WritePointerToMemory (lldb::addr_t process_address, lldb::addr_t address, Error &error); + void ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error); + void ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error); + void ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error); + + void GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error); + + lldb::ByteOrder GetByteOrder(); + uint32_t GetAddressByteSize(); + + // This function can return NULL. + ExecutionContextScope *GetBestExecutionContextScope(); + +protected: + // This function should only be used if you know you are using the JIT. + // Any other cases should use GetBestExecutionContextScope(). + lldb::ProcessWP GetProcessWP () + { + return m_process_wp; + } + +private: + struct Allocation + { + lldb::addr_t m_process_alloc; ///< The (unaligned) base for the remote allocation + lldb::addr_t m_process_start; ///< The base address of the allocation in the process + size_t m_size; ///< The size of the requested allocation + uint32_t m_permissions; ///< The access permissions on the memory in the process. In the host, the memory is always read/write. + uint8_t m_alignment; ///< The alignment of the requested allocation + DataBufferHeap m_data; + + ///< Flags + AllocationPolicy m_policy; + bool m_leak; + public: + Allocation (lldb::addr_t process_alloc, + lldb::addr_t process_start, + size_t size, + uint32_t permissions, + uint8_t alignment, + AllocationPolicy m_policy); + + Allocation () : + m_process_alloc (LLDB_INVALID_ADDRESS), + m_process_start (LLDB_INVALID_ADDRESS), + m_size (0), + m_permissions (0), + m_alignment (0), + m_data (), + m_policy (eAllocationPolicyInvalid), + m_leak (false) + { + } + }; + + lldb::ProcessWP m_process_wp; + lldb::TargetWP m_target_wp; + typedef std::map AllocationMap; + AllocationMap m_allocations; + + lldb::addr_t FindSpace (size_t size); + bool ContainsHostOnlyAllocations (); + AllocationMap::iterator FindAllocation (lldb::addr_t addr, size_t size); + bool IntersectsAllocation (lldb::addr_t addr, size_t size); +}; + +} + +#endif diff --git a/include/lldb/Expression/IRToDWARF.h b/include/lldb/Expression/IRToDWARF.h new file mode 100644 index 00000000000..43dc99d6d47 --- /dev/null +++ b/include/lldb/Expression/IRToDWARF.h @@ -0,0 +1,111 @@ +//===-- IRToDWARF.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_IRToDWARF_h_ +#define liblldb_IRToDWARF_h_ + +#include "llvm/Pass.h" +#include "llvm/PassManager.h" + +#include "lldb/lldb-public.h" + +class Relocator; +//---------------------------------------------------------------------- +/// @class IRToDWARF IRToDWARF.h "lldb/Expression/IRToDWARF.h" +/// @brief Transforms the IR for a function into a DWARF location expression +/// +/// 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. +/// +/// IRToDWARF makes the first possible, by traversing the control flow +/// graph and writing the code for each basic block out as location +/// expression bytecode. To ensure that the links between the basic blocks +/// remain intact, it uses a relocator that records the location of every +/// location expression instruction that has a relocatable operand, the +/// target of that operand (as a basic block), and the mapping of each basic +/// block to an actual location. After all code has been written out, the +/// relocator post-processes it and performs all necessary relocations. +//---------------------------------------------------------------------- +class IRToDWARF : public llvm::ModulePass +{ +public: + //------------------------------------------------------------------ + /// Constructor + /// + /// @param[in] local_vars + /// A list of variables to populate with the local variables this + /// expression uses. + /// + /// @param[in] decl_map + /// The list of externally-referenced variables for the expression, + /// for use in looking up globals. + /// + /// @param[in] stream + /// The stream to dump DWARF bytecode onto. + /// + /// @param[in] func_name + /// The name of the function to translate to DWARF. + //------------------------------------------------------------------ + IRToDWARF(lldb_private::ClangExpressionVariableList &local_vars, + lldb_private::ClangExpressionDeclMap *decl_map, + lldb_private::StreamString &strm, + const char* func_name = "$__lldb_expr"); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual ~IRToDWARF(); + + //------------------------------------------------------------------ + /// 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 converted to a location + /// expression. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool runOnModule(llvm::Module &M); + + //------------------------------------------------------------------ + /// Interface stub + //------------------------------------------------------------------ + void assignPassManager(llvm::PMStack &PMS, + llvm::PassManagerType T = llvm::PMT_ModulePassManager); + + //------------------------------------------------------------------ + /// Returns PMT_ModulePassManager + //------------------------------------------------------------------ + llvm::PassManagerType getPotentialPassManagerType() const; +private: + //------------------------------------------------------------------ + /// Run this IR transformer on a single basic block + /// + /// @param[in] BB + /// The basic block to transform. + /// + /// @param[in] Relocator + /// The relocator to use when registering branches. + /// + /// @return + /// True on success; false otherwise + //------------------------------------------------------------------ + bool runOnBasicBlock(llvm::BasicBlock &BB, Relocator &Relocator); + + std::string m_func_name; ///< The name of the function to translate + lldb_private::ClangExpressionVariableList &m_local_vars; ///< The list of local variables to populate while transforming + lldb_private::ClangExpressionDeclMap *m_decl_map; ///< The list of external variables + lldb_private::StreamString &m_strm; ///< The stream to write bytecode to +}; + +#endif diff --git a/include/lldb/Expression/Materializer.h b/include/lldb/Expression/Materializer.h new file mode 100644 index 00000000000..208a0813392 --- /dev/null +++ b/include/lldb/Expression/Materializer.h @@ -0,0 +1,173 @@ +//===-- Materializer.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_Materializer_h +#define lldb_Materializer_h + +#include "lldb/lldb-private-types.h" +#include "lldb/Core/Error.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/StackFrame.h" + +#include + +namespace lldb_private +{ + +class Materializer +{ +public: + Materializer (); + ~Materializer (); + + class Dematerializer + { + public: + Dematerializer () : + m_materializer(NULL), + m_map(NULL), + m_process_address(LLDB_INVALID_ADDRESS) + { + } + + ~Dematerializer () + { + Wipe (); + } + + void Dematerialize (Error &err, + lldb::ClangExpressionVariableSP &result_sp, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom); + + void Wipe (); + + bool IsValid () + { + return m_materializer && m_map && (m_process_address != LLDB_INVALID_ADDRESS); + } + private: + friend class Materializer; + + Dematerializer (Materializer &materializer, + lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address) : + m_materializer(&materializer), + m_map(&map), + m_process_address(process_address) + { + if (frame_sp) + { + m_thread_wp = frame_sp->GetThread(); + m_stack_id = frame_sp->GetStackID(); + } + } + + Materializer *m_materializer; + lldb::ThreadWP m_thread_wp; + StackID m_stack_id; + IRMemoryMap *m_map; + lldb::addr_t m_process_address; + }; + + typedef std::shared_ptr DematerializerSP; + typedef std::weak_ptr DematerializerWP; + + DematerializerSP Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err); + + uint32_t AddPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp, Error &err); + uint32_t AddVariable (lldb::VariableSP &variable_sp, Error &err); + uint32_t AddResultVariable (const TypeFromUser &type, bool is_lvalue, bool keep_in_memory, Error &err); + uint32_t AddSymbol (const Symbol &symbol_sp, Error &err); + uint32_t AddRegister (const RegisterInfo ®ister_info, Error &err); + + uint32_t GetStructAlignment () + { + return m_struct_alignment; + } + + uint32_t GetStructByteSize () + { + return m_current_offset; + } + + uint32_t GetResultOffset () + { + if (m_result_entity) + return m_result_entity->GetOffset(); + else + return UINT32_MAX; + } + + class Entity + { + public: + Entity () : + m_alignment(1), + m_size(0), + m_offset(0) + { + } + + virtual ~Entity () + { + } + + virtual void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) = 0; + virtual void Dematerialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, + lldb::addr_t frame_top, lldb::addr_t frame_bottom, Error &err) = 0; + virtual void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) = 0; + virtual void Wipe (IRMemoryMap &map, lldb::addr_t process_address) = 0; + + uint32_t GetAlignment () + { + return m_alignment; + } + + uint32_t GetSize () + { + return m_size; + } + + uint32_t GetOffset () + { + return m_offset; + } + + void SetOffset (uint32_t offset) + { + m_offset = offset; + } + protected: + void SetSizeAndAlignmentFromType (ClangASTType &type); + + uint32_t m_alignment; + uint32_t m_size; + uint32_t m_offset; + }; + +private: + uint32_t AddStructMember (Entity &entity); + + typedef std::unique_ptr EntityUP; + typedef std::vector EntityVector; + + DematerializerWP m_dematerializer_wp; + EntityVector m_entities; + Entity *m_result_entity; + uint32_t m_current_offset; + uint32_t m_struct_alignment; +}; + +} + +#endif diff --git a/include/lldb/Host/Condition.h b/include/lldb/Host/Condition.h new file mode 100644 index 00000000000..98439ee2ebd --- /dev/null +++ b/include/lldb/Host/Condition.h @@ -0,0 +1,124 @@ +//===-- Condition.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DBCondition_h_ +#define liblldb_DBCondition_h_ +#if defined(__cplusplus) + + +#include +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class TimeValue; + +//---------------------------------------------------------------------- +/// @class Condition Condition.h "lldb/Host/Condition.h" +/// @brief A C++ wrapper class for pthread condition variables. +/// +/// A class that wraps up a pthread condition (pthread_cond_t). The +/// class will create a pthread condition when an instance is +/// constructed, and detroy it when it is destructed. It also provides +/// access to the standard pthread condition calls. +//---------------------------------------------------------------------- +class Condition +{ +public: + + //------------------------------------------------------------------ + /// Default constructor + /// + /// The default constructor will initialize a new pthread condition + /// and maintain the condition in the object state. + //------------------------------------------------------------------ + Condition (); + + //------------------------------------------------------------------ + /// Destructor + /// + /// Destroys the pthread condition that the object owns. + //------------------------------------------------------------------ + ~Condition (); + + //------------------------------------------------------------------ + /// Unblock all threads waiting for a condition variable + /// + /// @return + /// The return value from \c pthread_cond_broadcast() + //------------------------------------------------------------------ + int + Broadcast (); + + //------------------------------------------------------------------ + /// Unblocks one thread waiting for the condition variable + /// + /// @return + /// The return value from \c pthread_cond_signal() + //------------------------------------------------------------------ + int + Signal (); + + //------------------------------------------------------------------ + /// Wait for the condition variable to be signaled. + /// + /// The Wait() function atomically blocks the current thread + /// waiting on this object's condition variable, and unblocks + /// \a mutex. The waiting thread unblocks only after another thread + /// signals or broadcasts this object's condition variable. + /// + /// If \a abstime is non-NULL, this function will return when the + /// system time reaches the time specified in \a abstime if the + /// condition variable doesn't get unblocked. If \a abstime is NULL + /// this function will wait for an infinite amount of time for the + /// condition variable to be unblocked. + /// + /// The current thread re-acquires the lock on \a mutex following + /// the wait. + /// + /// @param[in] mutex + /// The mutex to use in the \c pthread_cond_timedwait() or + /// \c pthread_cond_wait() calls. + /// + /// @param[in] abstime + /// An absolute time at which to stop waiting if non-NULL, else + /// wait an infinite amount of time for the condition variable + /// toget signaled. + /// + /// @param[out] timed_out + /// If not NULL, will be set to true if the wait timed out, and + // false otherwise. + /// + /// @see Condition::Broadcast() + /// @see Condition::Signal() + //------------------------------------------------------------------ + int + Wait (Mutex &mutex, const TimeValue *abstime = NULL, bool *timed_out = NULL); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + pthread_cond_t m_condition; ///< The condition variable. + + //------------------------------------------------------------------ + /// Get accessor to the pthread condition object. + /// + /// @return + /// A pointer to the condition variable owned by this object. + //------------------------------------------------------------------ + pthread_cond_t * + GetCondition (); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif + diff --git a/include/lldb/Host/Config.h b/include/lldb/Host/Config.h new file mode 100644 index 00000000000..2d5d39baac3 --- /dev/null +++ b/include/lldb/Host/Config.h @@ -0,0 +1,35 @@ +//===-- Config.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Config_h_ +#define liblldb_Config_h_ + +#if defined(__APPLE__) + +#include "lldb/Host/macosx/Config.h" + +#elif defined(__linux__) + +#include "lldb/Host/linux/Config.h" + +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) + +#include "lldb/Host/freebsd/Config.h" + +#elif defined(__MINGW__) || defined (__MINGW32__) + +#include "lldb/Host/mingw/Config.h" + +#else + +#error undefined platform + +#endif + +#endif // #ifndef liblldb_Config_h_ diff --git a/include/lldb/Host/DynamicLibrary.h b/include/lldb/Host/DynamicLibrary.h new file mode 100644 index 00000000000..1fcc7d1883c --- /dev/null +++ b/include/lldb/Host/DynamicLibrary.h @@ -0,0 +1,51 @@ +//===-- DynamicLibrary.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLibrary_h_ +#define liblldb_DynamicLibrary_h_ + +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" + +namespace lldb_private { + +class DynamicLibrary +{ +public: + DynamicLibrary (const FileSpec& spec, uint32_t options = Host::eDynamicLibraryOpenOptionLazy | + Host::eDynamicLibraryOpenOptionLocal | + Host::eDynamicLibraryOpenOptionLimitGetSymbol); + + ~DynamicLibrary (); + + template + T GetSymbol (const char* name) + { + Error err; + if (!m_handle) + return (T)NULL; + void* symbol = Host::DynamicLibraryGetSymbol (m_handle, name, err); + if (!symbol) + return (T)NULL; + return (T)symbol; + } + + bool + IsValid (); + +private: + lldb_private::FileSpec m_filespec; + void* m_handle; + + DISALLOW_COPY_AND_ASSIGN (DynamicLibrary); +}; + +} // namespace lldb_private + +#endif // liblldb_DynamicLibrary_h_ diff --git a/include/lldb/Host/Endian.h b/include/lldb/Host/Endian.h new file mode 100644 index 00000000000..610f3ce95c4 --- /dev/null +++ b/include/lldb/Host/Endian.h @@ -0,0 +1,33 @@ +//===-- Endian.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_host_endian_h_ +#define liblldb_host_endian_h_ + +#include "lldb/lldb-enumerations.h" + +namespace lldb { + +namespace endian { + + static union EndianTest + { + uint32_t num; + uint8_t bytes[sizeof(uint32_t)]; + } const endianTest = { (uint16_t)0x01020304 }; + + inline ByteOrder InlHostByteOrder() { return (ByteOrder)endianTest.bytes[0]; } + +// ByteOrder const InlHostByteOrder = (ByteOrder)endianTest.bytes[0]; +} + +} + +#endif // liblldb_host_endian_h_ + diff --git a/include/lldb/Host/File.h b/include/lldb/Host/File.h new file mode 100644 index 00000000000..df7fe92cccb --- /dev/null +++ b/include/lldb/Host/File.h @@ -0,0 +1,500 @@ +//===-- File.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_File_h_ +#define liblldb_File_h_ +#if defined(__cplusplus) + +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class File File.h "lldb/Host/File.h" +/// @brief A file class. +/// +/// A file class that divides abstracts the LLDB core from host file +/// functionality. +//---------------------------------------------------------------------- +class File +{ +public: + static int kInvalidDescriptor; + static FILE * kInvalidStream; + + enum OpenOptions + { + eOpenOptionRead = (1u << 0), // Open file for reading + eOpenOptionWrite = (1u << 1), // Open file for writing + eOpenOptionAppend = (1u << 2), // Don't truncate file when opening, append to end of file + eOpenOptionTruncate = (1u << 3), // Truncate file when opening + eOpenOptionNonBlocking = (1u << 4), // File reads + eOpenOptionCanCreate = (1u << 5), // Create file if doesn't already exist + eOpenOptionCanCreateNewOnly = (1u << 6) // Can create file only if it doesn't already exist + }; + + enum Permissions + { + ePermissionsUserRead = (1u << 0), + ePermissionsUserWrite = (1u << 1), + ePermissionsUserExecute = (1u << 2), + ePermissionsGroupRead = (1u << 3), + ePermissionsGroupWrite = (1u << 4), + ePermissionsGroupExecute = (1u << 5), + ePermissionsWorldRead = (1u << 6), + ePermissionsWorldWrite = (1u << 7), + ePermissionsWorldExecute = (1u << 8), + + ePermissionsUserRW = (ePermissionsUserRead | ePermissionsUserWrite | 0 ), + ePermissionsUserRX = (ePermissionsUserRead | 0 | ePermissionsUserExecute ), + ePermissionsUserRWX = (ePermissionsUserRead | ePermissionsUserWrite | ePermissionsUserExecute ), + + ePermissionsGroupRW = (ePermissionsGroupRead | ePermissionsGroupWrite | 0 ), + ePermissionsGroupRX = (ePermissionsGroupRead | 0 | ePermissionsGroupExecute ), + ePermissionsGroupRWX = (ePermissionsGroupRead | ePermissionsGroupWrite | ePermissionsGroupExecute ), + + ePermissionsWorldRW = (ePermissionsWorldRead | ePermissionsWorldWrite | 0 ), + ePermissionsWorldRX = (ePermissionsWorldRead | 0 | ePermissionsWorldExecute ), + ePermissionsWorldRWX = (ePermissionsWorldRead | ePermissionsWorldWrite | ePermissionsWorldExecute ), + + ePermissionsEveryoneR = (ePermissionsUserRead | ePermissionsGroupRead | ePermissionsWorldRead ), + ePermissionsEveryoneW = (ePermissionsUserWrite | ePermissionsGroupWrite | ePermissionsWorldWrite ), + ePermissionsEveryoneX = (ePermissionsUserExecute | ePermissionsGroupExecute | ePermissionsWorldExecute ), + + ePermissionsEveryoneRW = (ePermissionsEveryoneR | ePermissionsEveryoneW | 0 ), + ePermissionsEveryoneRX = (ePermissionsEveryoneR | 0 | ePermissionsEveryoneX ), + ePermissionsEveryoneRWX = (ePermissionsEveryoneR | ePermissionsEveryoneW | ePermissionsEveryoneX ), + ePermissionsDefault = (ePermissionsUserRW | ePermissionsGroupRead) + }; + + File() : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) + { + } + + File (FILE *fh, bool transfer_ownership) : + m_descriptor (kInvalidDescriptor), + m_stream (fh), + m_options (0), + m_owned (transfer_ownership) + { + } + + File (const File &rhs); + + File & + operator= (const File &rhs); + //------------------------------------------------------------------ + /// Constructor with path. + /// + /// Takes a path to a file which can be just a filename, or a full + /// path. If \a path is not NULL or empty, this function will call + /// File::Open (const char *path, uint32_t options, uint32_t permissions). + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @param[in] options + /// Options to use when opening (see File::OpenOptions) + /// + /// @param[in] permissions + /// Options to use when opening (see File::Permissions) + /// + /// @see File::Open (const char *path, uint32_t options, uint32_t permissions) + //------------------------------------------------------------------ + File (const char *path, + uint32_t options, + uint32_t permissions = ePermissionsDefault); + + + File (int fd, bool tranfer_ownership) : + m_descriptor (fd), + m_stream (kInvalidStream), + m_options (0), + m_owned (tranfer_ownership) + { + } + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual in case this class is subclassed. + //------------------------------------------------------------------ + virtual + ~File (); + + bool + IsValid () const + { + return DescriptorIsValid() || StreamIsValid(); + } + + //------------------------------------------------------------------ + /// Convert to pointer operator. + /// + /// This allows code to check a File object to see if it + /// contains anything valid using code such as: + /// + /// @code + /// File file(...); + /// if (file) + /// { ... + /// @endcode + /// + /// @return + /// A pointer to this object if either the directory or filename + /// is valid, NULL otherwise. + //------------------------------------------------------------------ + operator + bool () const + { + return DescriptorIsValid() || StreamIsValid(); + } + + //------------------------------------------------------------------ + /// Logical NOT operator. + /// + /// This allows code to check a File object to see if it is + /// invalid using code such as: + /// + /// @code + /// File file(...); + /// if (!file) + /// { ... + /// @endcode + /// + /// @return + /// Returns \b true if the object has an empty directory and + /// filename, \b false otherwise. + //------------------------------------------------------------------ + bool + operator! () const + { + return !DescriptorIsValid() && !StreamIsValid(); + } + + //------------------------------------------------------------------ + /// Get the file spec for this file. + /// + /// @return + /// A reference to the file specification object. + //------------------------------------------------------------------ + Error + GetFileSpec (FileSpec &file_spec) const; + + //------------------------------------------------------------------ + /// Open a file for read/writing with the specified options. + /// + /// Takes a path to a file which can be just a filename, or a full + /// path. + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @param[in] options + /// Options to use when opening (see File::OpenOptions) + /// + /// @param[in] permissions + /// Options to use when opening (see File::Permissions) + //------------------------------------------------------------------ + Error + Open (const char *path, + uint32_t options, + uint32_t permissions = ePermissionsDefault); + + Error + Close (); + + Error + Duplicate (const File &rhs); + + int + GetDescriptor() const; + + void + SetDescriptor(int fd, bool transfer_ownership); + + FILE * + GetStream (); + + void + SetStream (FILE *fh, bool transfer_ownership); + + //------------------------------------------------------------------ + /// Read bytes from a file from the current file position. + /// + /// NOTE: This function is NOT thread safe. Use the read function + /// that takes an "off_t &offset" to ensure correct operation in + /// multi-threaded environments. + /// + /// @param[in] buf + /// A buffer where to put the bytes that are read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to read form the current file position + /// which gets modified with the number of bytes that were read. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Read (void *buf, size_t &num_bytes); + + //------------------------------------------------------------------ + /// Write bytes to a file at the current file position. + /// + /// NOTE: This function is NOT thread safe. Use the write function + /// that takes an "off_t &offset" to ensure correct operation in + /// multi-threaded environments. + /// + /// @param[in] buf + /// A buffer where to put the bytes that are read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to write to the current file position + /// which gets modified with the number of bytes that were + /// written. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Write (const void *buf, size_t &num_bytes); + + //------------------------------------------------------------------ + /// Seek to an offset relative to the beginning of the file. + /// + /// NOTE: This function is NOT thread safe, other threads that + /// access this object might also change the current file position. + /// For thread safe reads and writes see the following functions: + /// @see File::Read (void *, size_t, off_t &) + /// @see File::Write (const void *, size_t, off_t &) + /// + /// @param[in] offset + /// The offset to seek to within the file relative to the + /// beginning of the file. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// + /// @return + /// The resulting seek offset, or -1 on error. + //------------------------------------------------------------------ + off_t + SeekFromStart (off_t offset, Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Seek to an offset relative to the current file position. + /// + /// NOTE: This function is NOT thread safe, other threads that + /// access this object might also change the current file position. + /// For thread safe reads and writes see the following functions: + /// @see File::Read (void *, size_t, off_t &) + /// @see File::Write (const void *, size_t, off_t &) + /// + /// @param[in] offset + /// The offset to seek to within the file relative to the + /// current file position. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// + /// @return + /// The resulting seek offset, or -1 on error. + //------------------------------------------------------------------ + off_t + SeekFromCurrent (off_t offset, Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Seek to an offset relative to the end of the file. + /// + /// NOTE: This function is NOT thread safe, other threads that + /// access this object might also change the current file position. + /// For thread safe reads and writes see the following functions: + /// @see File::Read (void *, size_t, off_t &) + /// @see File::Write (const void *, size_t, off_t &) + /// + /// @param[in/out] offset + /// The offset to seek to within the file relative to the + /// end of the file which gets filled in the the resulting + /// absolute file offset. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// + /// @return + /// The resulting seek offset, or -1 on error. + //------------------------------------------------------------------ + off_t + SeekFromEnd (off_t offset, Error *error_ptr = NULL); + + //------------------------------------------------------------------ + /// Read bytes from a file from the specified file offset. + /// + /// NOTE: This function is thread safe in that clients manager their + /// own file position markers and reads on other threads won't mess + /// up the current read. + /// + /// @param[in] buf + /// A buffer where to put the bytes that are read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to read form the current file position + /// which gets modified with the number of bytes that were read. + /// + /// @param[in/out] offset + /// The offset within the file from which to read \a num_bytes + /// bytes. This offset gets incremented by the number of bytes + /// that were read. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Read (void *dst, size_t &num_bytes, off_t &offset); + + //------------------------------------------------------------------ + /// Read bytes from a file from the specified file offset. + /// + /// NOTE: This function is thread safe in that clients manager their + /// own file position markers and reads on other threads won't mess + /// up the current read. + /// + /// @param[in/out] num_bytes + /// The number of bytes to read form the current file position + /// which gets modified with the number of bytes that were read. + /// + /// @param[in/out] offset + /// The offset within the file from which to read \a num_bytes + /// bytes. This offset gets incremented by the number of bytes + /// that were read. + /// + /// @param[in] null_terminate + /// Ensure that the data that is read is terminated with a NULL + /// character so that the data can be used as a C string. + /// + /// @param[out] data_buffer_sp + /// A data buffer to create and fill in that will contain any + /// data that is read from the file. This buffer will be reset + /// if an error occurs. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Read (size_t &num_bytes, + off_t &offset, + bool null_terminate, + lldb::DataBufferSP &data_buffer_sp); + + //------------------------------------------------------------------ + /// Write bytes to a file at the specified file offset. + /// + /// NOTE: This function is thread safe in that clients manager their + /// own file position markers, though clients will need to implement + /// their own locking externally to avoid multiple people writing + /// to the file at the same time. + /// + /// @param[in] buf + /// A buffer containing the bytes to write. + /// + /// @param[in/out] num_bytes + /// The number of bytes to write to the file at offset \a offset. + /// \a num_bytes gets modified with the number of bytes that + /// were read. + /// + /// @param[in/out] offset + /// The offset within the file at which to write \a num_bytes + /// bytes. This offset gets incremented by the number of bytes + /// that were written. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Write (const void *src, size_t &num_bytes, off_t &offset); + + //------------------------------------------------------------------ + /// Flush the current stream + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Flush (); + + //------------------------------------------------------------------ + /// Sync to disk. + /// + /// @return + /// An error object that indicates success or the reason for + /// failure. + //------------------------------------------------------------------ + Error + Sync (); + + //------------------------------------------------------------------ + /// Output printf formatted output to the stream. + /// + /// Print some formatted output to the stream. + /// + /// @param[in] format + /// A printf style format string. + /// + /// @param[in] ... + /// Variable arguments that are needed for the printf style + /// format string \a format. + //------------------------------------------------------------------ + size_t + Printf (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + size_t + PrintfVarArg(const char *format, va_list args); + +protected: + + + bool + DescriptorIsValid () const + { + return m_descriptor >= 0; + } + + bool + StreamIsValid () const + { + return m_stream != kInvalidStream; + } + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + int m_descriptor; + FILE *m_stream; + uint32_t m_options; + bool m_owned; +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_File_h_ diff --git a/include/lldb/Host/FileSpec.h b/include/lldb/Host/FileSpec.h new file mode 100644 index 00000000000..c58be9ec09d --- /dev/null +++ b/include/lldb/Host/FileSpec.h @@ -0,0 +1,692 @@ +//===-- FileSpec.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_FileSpec_h_ +#define liblldb_FileSpec_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/Host/TimeValue.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FileSpec FileSpec.h "lldb/Host/FileSpec.h" +/// @brief A file utility class. +/// +/// A file specification class that divides paths up into a directory +/// and basename. These string values of the paths are put into uniqued +/// string pools for fast comparisons and efficient memory usage. +/// +/// Another reason the paths are split into the directory and basename +/// is to allow efficient debugger searching. Often in a debugger the +/// user types in the basename of the file, for example setting a +/// breakpoint by file and line, or specifying a module (shared library) +/// to limit the scope in which to execute a command. The user rarely +/// types in a full path. When the paths are already split up, it makes +/// it easy for us to compare only the basenames of a lot of file +/// specifications without having to split up the file path each time +/// to get to the basename. +//---------------------------------------------------------------------- +class FileSpec +{ +public: + typedef enum FileType + { + eFileTypeInvalid = -1, + eFileTypeUnknown = 0, + eFileTypeDirectory, + eFileTypePipe, + eFileTypeRegular, + eFileTypeSocket, + eFileTypeSymbolicLink, + eFileTypeOther + } FileType; + + FileSpec(); + + //------------------------------------------------------------------ + /// Constructor with path. + /// + /// Takes a path to a file which can be just a filename, or a full + /// path. If \a path is not NULL or empty, this function will call + /// FileSpec::SetFile (const char *path, bool resolve). + /// + /// @param[in] path + /// The full or partial path to a file. + /// + /// @param[in] resolve_path + /// If \b true, then we resolve the path with realpath, + /// if \b false we trust the path is in canonical form already. + /// + /// @see FileSpec::SetFile (const char *path, bool resolve) + //------------------------------------------------------------------ + explicit FileSpec (const char *path, bool resolve_path); + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the uniqued directory and filename strings from + /// \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to copy. + //------------------------------------------------------------------ + FileSpec (const FileSpec& rhs); + + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the uniqued directory and filename strings from + /// \a rhs if it is not NULL. + /// + /// @param[in] rhs + /// A const FileSpec object pointer to copy if non-NULL. + //------------------------------------------------------------------ + FileSpec (const FileSpec* rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~FileSpec (); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Makes a copy of the uniqued directory and filename strings from + /// \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to assign to this object. + /// + /// @return + /// A const reference to this object. + //------------------------------------------------------------------ + const FileSpec& + operator= (const FileSpec& rhs); + + //------------------------------------------------------------------ + /// Equal to operator + /// + /// Tests if this object is equal to \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to compare this object + /// to. + /// + /// @return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + operator== (const FileSpec& rhs) const; + + //------------------------------------------------------------------ + /// Not equal to operator + /// + /// Tests if this object is not equal to \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to compare this object + /// to. + /// + /// @return + /// \b true if this object is equal to \a rhs, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + operator!= (const FileSpec& rhs) const; + + //------------------------------------------------------------------ + /// Less than to operator + /// + /// Tests if this object is less than \a rhs. + /// + /// @param[in] rhs + /// A const FileSpec object reference to compare this object + /// to. + /// + /// @return + /// \b true if this object is less than \a rhs, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + operator< (const FileSpec& rhs) const; + + //------------------------------------------------------------------ + /// Convert to pointer operator. + /// + /// This allows code to check a FileSpec object to see if it + /// contains anything valid using code such as: + /// + /// @code + /// FileSpec file_spec(...); + /// if (file_spec) + /// { ... + /// @endcode + /// + /// @return + /// A pointer to this object if either the directory or filename + /// is valid, NULL otherwise. + //------------------------------------------------------------------ + operator bool() const; + + //------------------------------------------------------------------ + /// Logical NOT operator. + /// + /// This allows code to check a FileSpec object to see if it is + /// invalid using code such as: + /// + /// @code + /// FileSpec file_spec(...); + /// if (!file_spec) + /// { ... + /// @endcode + /// + /// @return + /// Returns \b true if the object has an empty directory and + /// filename, \b false otherwise. + //------------------------------------------------------------------ + bool + operator! () const; + + //------------------------------------------------------------------ + /// Clears the object state. + /// + /// Clear this object by releasing both the directory and filename + /// string values and reverting them to empty strings. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Compare two FileSpec objects. + /// + /// If \a full is true, then both the directory and the filename + /// must match. If \a full is false, then the directory names for + /// \a lhs and \a rhs are only compared if they are both not empty. + /// This allows a FileSpec object to only contain a filename + /// and it can match FileSpec objects that have matching + /// filenames with different paths. + /// + /// @param[in] lhs + /// A const reference to the Left Hand Side object to compare. + /// + /// @param[in] rhs + /// A const reference to the Right Hand Side object to compare. + /// + /// @param[in] full + /// If true, then both the directory and filenames will have to + /// match for a compare to return zero (equal to). If false + /// and either directory from \a lhs or \a rhs is empty, then + /// only the filename will be compared, else a full comparison + /// is done. + /// + /// @return + /// @li -1 if \a lhs is less than \a rhs + /// @li 0 if \a lhs is equal to \a rhs + /// @li 1 if \a lhs is greater than \a rhs + //------------------------------------------------------------------ + static int + Compare (const FileSpec& lhs, const FileSpec& rhs, bool full); + + static bool + Equal (const FileSpec& a, const FileSpec& b, bool full); + + //------------------------------------------------------------------ + /// Dump this object to a Stream. + /// + /// Dump the object to the supplied stream \a s. If the object + /// contains a valid directory name, it will be displayed followed + /// by a directory delimiter, and the filename. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s) const; + + //------------------------------------------------------------------ + /// Existence test. + /// + /// @return + /// \b true if the file exists on disk, \b false otherwise. + //------------------------------------------------------------------ + bool + Exists () const; + + + //------------------------------------------------------------------ + /// Expanded existence test. + /// + /// Call into the Host to see if it can help find the file (e.g. by + /// searching paths set in the environment, etc.). + /// + /// If found, sets the value of m_directory to the directory where + /// the file was found. + /// + /// @return + /// \b true if was able to find the file using expanded search + /// methods, \b false otherwise. + //------------------------------------------------------------------ + bool + ResolveExecutableLocation (); + + //------------------------------------------------------------------ + /// Canonicalize this file path (basically running the static + /// FileSpec::Resolve method on it). Useful if you asked us not to + /// resolve the file path when you set the file. + //------------------------------------------------------------------ + bool + ResolvePath (); + + uint64_t + GetByteSize() const; + + //------------------------------------------------------------------ + /// Directory string get accessor. + /// + /// @return + /// A reference to the directory string object. + //------------------------------------------------------------------ + ConstString & + GetDirectory (); + + //------------------------------------------------------------------ + /// Directory string const get accessor. + /// + /// @return + /// A const reference to the directory string object. + //------------------------------------------------------------------ + const ConstString & + GetDirectory () const; + + //------------------------------------------------------------------ + /// Filename string get accessor. + /// + /// @return + /// A reference to the filename string object. + //------------------------------------------------------------------ + ConstString & + GetFilename (); + + //------------------------------------------------------------------ + /// Filename string const get accessor. + /// + /// @return + /// A const reference to the filename string object. + //------------------------------------------------------------------ + const ConstString & + GetFilename () const; + + //------------------------------------------------------------------ + /// 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 + IsSourceImplementationFile () const; + + //------------------------------------------------------------------ + /// Returns true if the filespec represents path that is relative + /// path to the current working directory. + /// + /// @return + /// \b true if the filespec represents a current working + /// directory relative path, \b false otherwise. + //------------------------------------------------------------------ + bool + IsRelativeToCurrentWorkingDirectory () const; + + TimeValue + GetModificationTime () const; + + //------------------------------------------------------------------ + /// Extract the full path to the file. + /// + /// Extract the directory and path into a fixed buffer. This is + /// needed as the directory and path are stored in separate string + /// values. + /// + /// @param[out] path + /// The buffer in which to place the extracted full path. + /// + /// @param[in] max_path_length + /// The maximum length of \a path. + /// + /// @return + /// Returns the number of characters that would be needed to + /// properly copy the full path into \a path. If the returned + /// number is less than \a max_path_length, then the path is + /// properly copied and terminated. If the return value is + /// >= \a max_path_length, then the path was truncated (but is + /// still NULL terminated). + //------------------------------------------------------------------ + size_t + GetPath (char *path, size_t max_path_length) const; + + //------------------------------------------------------------------ + /// Extract the full path to the file. + /// + /// Extract the directory and path into a std::string, which is returned. + /// + /// @return + /// Returns a std::string with the directory and filename + /// concatenated. + //------------------------------------------------------------------ + std::string + GetPath () const; + + //------------------------------------------------------------------ + /// Extract the extension of the file. + /// + /// Returns a ConstString that represents the extension of the filename + /// for this FileSpec object. If this object does not represent a file, + /// or the filename has no extension, ConstString(NULL) is returned. + /// The dot ('.') character is not returned as part of the extension + /// + /// @return + /// Returns the extension of the file as a ConstString object. + //------------------------------------------------------------------ + ConstString + GetFileNameExtension () const; + + //------------------------------------------------------------------ + /// Return the filename without the extension part + /// + /// Returns a ConstString that represents the filename of this object + /// without the extension part (e.g. for a file named "foo.bar", "foo" + /// is returned) + /// + /// @return + /// Returns the filename without extension + /// as a ConstString object. + //------------------------------------------------------------------ + ConstString + GetFileNameStrippingExtension () const; + + FileType + GetFileType () const; + + bool + IsDirectory () const + { + return GetFileType() == FileSpec::eFileTypeDirectory; + } + + bool + IsPipe () const + { + return GetFileType() == FileSpec::eFileTypePipe; + } + + bool + IsRegularFile () const + { + return GetFileType() == FileSpec::eFileTypeRegular; + } + + bool + IsSocket () const + { + return GetFileType() == FileSpec::eFileTypeSocket; + } + + bool + IsSymbolicLink () const + { + return GetFileType() == FileSpec::eFileTypeSymbolicLink; + } + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// 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. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Memory map part of, or the entire contents of, a file. + /// + /// Returns a shared pointer to a data buffer that contains all or + /// part of the contents of a file. The data is memory mapped and + /// will lazily page in data from the file as memory is accessed. + /// The data that is mappped will start \a offset bytes into the + /// file, and \a length bytes will be mapped. If \a length is + /// greater than the number of bytes available in the file starting + /// at \a offset, the number of bytes will be appropriately + /// truncated. The final number of bytes that get mapped can be + /// verified using the DataBuffer::GetByteSize() function on the return + /// shared data pointer object contents. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// A shared pointer to the memeory mapped data. This shared + /// pointer can contain a NULL DataBuffer pointer, so the contained + /// pointer must be checked prior to using it. + //------------------------------------------------------------------ + lldb::DataBufferSP + MemoryMapFileContents (off_t offset = 0, size_t length = SIZE_MAX) const; + + //------------------------------------------------------------------ + /// Read part of, or the entire contents of, a file into a heap based data buffer. + /// + /// Returns a shared pointer to a data buffer that contains all or + /// part of the contents of a file. The data copies into a heap based + /// buffer that lives in the DataBuffer shared pointer object returned. + /// The data that is cached will start \a offset bytes into the + /// file, and \a length bytes will be mapped. If \a length is + /// greater than the number of bytes available in the file starting + /// at \a offset, the number of bytes will be appropriately + /// truncated. The final number of bytes that get mapped can be + /// verified using the DataBuffer::GetByteSize() function. + /// + /// @param[in] offset + /// The offset in bytes from the beginning of the file where + /// memory mapping should begin. + /// + /// @param[in] length + /// The size in bytes that should be mapped starting \a offset + /// bytes into the file. If \a length is \c SIZE_MAX, map + /// as many bytes as possible. + /// + /// @return + /// A shared pointer to the memeory mapped data. This shared + /// pointer can contain a NULL DataBuffer pointer, so the contained + /// pointer must be checked prior to using it. + //------------------------------------------------------------------ + lldb::DataBufferSP + ReadFileContents (off_t offset = 0, size_t length = SIZE_MAX, Error *error_ptr = NULL) const; + + size_t + ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const; + + + //------------------------------------------------------------------ + /// Read the entire contents of a file as data that can be used + /// as a C string. + /// + /// Read the entire contents of a file and ensure that the data + /// is NULL terminated so it can be used as a C string. + /// + /// @return + /// A shared pointer to the data. This shared pointer can + /// contain a NULL DataBuffer pointer, so the contained pointer + /// must be checked prior to using it. + //------------------------------------------------------------------ + lldb::DataBufferSP + ReadFileContentsAsCString(Error *error_ptr = NULL); + //------------------------------------------------------------------ + /// Change the file specificed with a new path. + /// + /// 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. + /// + /// @param[in] path + /// A full, partial, or relative path to a file. + /// + /// @param[in] resolve_path + /// If \b true, then we will try to resolve links the path using + /// the static FileSpec::Resolve. + //------------------------------------------------------------------ + void + SetFile (const char *path, bool resolve_path); + + bool + IsResolved () const + { + return m_is_resolved; + } + + //------------------------------------------------------------------ + /// Set if the file path has been resolved or not. + /// + /// If you know a file path is already resolved and avoided passing + /// a \b true parameter for any functions that take a "bool + /// resolve_path" parameter, you can set the value manually using + /// this call to make sure we don't try and resolve it later, or try + /// and resolve a path that has already been resolved. + /// + /// @param[in] is_resolved + /// A boolean value that will replace the current value that + /// indicates if the paths in this object have been resolved. + //------------------------------------------------------------------ + void + SetIsResolved (bool is_resolved) + { + m_is_resolved = is_resolved; + } + //------------------------------------------------------------------ + /// Read the file into an array of strings, one per line. + /// + /// Opens and reads the file in this object into an array of strings, + /// one string per line of the file. Returns a boolean indicating + /// success or failure. + /// + /// @param[out] lines + /// The string array into which to read the file. + /// + /// @result + /// Returns the number of lines that were read from the file. + //------------------------------------------------------------------ + size_t + ReadFileLines (STLStringArray &lines); + + //------------------------------------------------------------------ + /// Resolves user name and links in \a src_path, and writes the output + /// to \a dst_path. Note if the path pointed to by \a src_path does not + /// exist, the contents of \a src_path will be copied to \a dst_path + /// unchanged. + /// + /// @param[in] src_path + /// Input path to be resolved. + /// + /// @param[in] dst_path + /// Buffer to store the resolved path. + /// + /// @param[in] dst_len + /// Size of the buffer pointed to by dst_path. + /// + /// @result + /// The number of characters required to write the resolved path. If the + /// resolved path doesn't fit in dst_len, dst_len-1 characters will + /// be written to \a dst_path, but the actual required length will still be returned. + //------------------------------------------------------------------ + static size_t + Resolve (const char *src_path, char *dst_path, size_t dst_len); + + //------------------------------------------------------------------ + /// Resolves the user name at the beginning of \a src_path, and writes the output + /// to \a dst_path. Note, \a src_path can contain other path components after the + /// user name, they will be copied over, and if the path doesn't start with "~" it + /// will also be copied over to \a dst_path. + /// + /// @param[in] src_path + /// Input path to be resolved. + /// + /// @param[in] dst_path + /// Buffer to store the resolved path. + /// + /// @param[in] dst_len + /// Size of the buffer pointed to by dst_path. + /// + /// @result + /// The number of characters required to write the resolved path, or 0 if + /// the user name could not be found. If the + /// resolved path doesn't fit in dst_len, dst_len-1 characters will + /// be written to \a dst_path, but the actual required length will still be returned. + //------------------------------------------------------------------ + static size_t + ResolveUsername (const char *src_path, char *dst_path, size_t dst_len); + + static size_t + ResolvePartialUsername (const char *partial_name, StringList &matches); + + enum EnumerateDirectoryResult + { + eEnumerateDirectoryResultNext, // Enumerate next entry in the current directory + eEnumerateDirectoryResultEnter, // Recurse into the current entry if it is a directory or symlink, or next if not + eEnumerateDirectoryResultExit, // Exit from the current directory at the current level. + eEnumerateDirectoryResultQuit // Stop directory enumerations at any level + }; + + typedef EnumerateDirectoryResult (*EnumerateDirectoryCallbackType) (void *baton, + FileType file_type, + const FileSpec &spec +); + + static EnumerateDirectoryResult + EnumerateDirectory (const char *dir_path, + bool find_directories, + bool find_files, + bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + ConstString m_directory; ///< The uniqued directory path + ConstString m_filename; ///< The uniqued filename path + mutable bool m_is_resolved; ///< True if this path has been resolved. +}; + +//---------------------------------------------------------------------- +/// Dump a FileSpec object to a stream +//---------------------------------------------------------------------- +Stream& operator << (Stream& s, const FileSpec& f); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_FileSpec_h_ diff --git a/include/lldb/Host/Host.h b/include/lldb/Host/Host.h new file mode 100644 index 00000000000..547bdd5d637 --- /dev/null +++ b/include/lldb/Host/Host.h @@ -0,0 +1,502 @@ +//===-- Host.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Host_h_ +#define liblldb_Host_h_ +#if defined(__cplusplus) + +#include + +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Host Host.h "lldb/Host/Host.h" +/// @brief A class that provides host computer information. +/// +/// Host is a class that answers information about the host operating +/// system. +//---------------------------------------------------------------------- +class Host +{ +public: + typedef bool (*MonitorChildProcessCallback) (void *callback_baton, + lldb::pid_t pid, + bool exited, + int signal, // Zero for no signal + int status); // Exit value of process if signal is zero + + //------------------------------------------------------------------ + /// Start monitoring a child process. + /// + /// Allows easy monitoring of child processes. \a callback will be + /// called when the child process exits or if it gets a signal. The + /// callback will only be called with signals if \a monitor_signals + /// is \b true. \a callback will usually be called from another + /// thread so the callback function must be thread safe. + /// + /// When the callback gets called, the return value indicates if + /// minotoring should stop. If \b true is returned from \a callback + /// the information will be removed. If \b false is returned then + /// monitoring will continue. If the child process exits, the + /// monitoring will automatically stop after the callback returned + /// ragardless of the callback return value. + /// + /// @param[in] callback + /// A function callback to call when a child receives a signal + /// (if \a monitor_signals is true) or a child exits. + /// + /// @param[in] callback_baton + /// A void * of user data that will be pass back when + /// \a callback is called. + /// + /// @param[in] pid + /// The process ID of a child process to monitor, -1 for all + /// processes. + /// + /// @param[in] monitor_signals + /// If \b true the callback will get called when the child + /// process gets a signal. If \b false, the callback will only + /// get called if the child process exits. + /// + /// @return + /// A thread handle that can be used to cancel the thread that + /// was spawned to monitor \a pid. + /// + /// @see static void Host::StopMonitoringChildProcess (uint32_t) + //------------------------------------------------------------------ + static lldb::thread_t + StartMonitoringChildProcess (MonitorChildProcessCallback callback, + void *callback_baton, + lldb::pid_t pid, + bool monitor_signals); + + //------------------------------------------------------------------ + /// Get the host page size. + /// + /// @return + /// The size in bytes of a VM page on the host system. + //------------------------------------------------------------------ + static size_t + GetPageSize(); + + //------------------------------------------------------------------ + /// Returns the endianness of the host system. + /// + /// @return + /// Returns the endianness of the host system as a lldb::ByteOrder + /// enumeration. + //------------------------------------------------------------------ + static lldb::ByteOrder + GetByteOrder (); + + //------------------------------------------------------------------ + /// Returns the number of CPUs on this current host. + /// + /// @return + /// Number of CPUs on this current host, or zero if the number + /// of CPUs can't be determined on this host. + //------------------------------------------------------------------ + static uint32_t + GetNumberCPUS (); + + static bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update); + + static bool + GetOSBuildString (std::string &s); + + static bool + GetOSKernelDescription (std::string &s); + + static bool + GetHostname (std::string &s); + + static const char * + GetUserName (uint32_t uid, std::string &user_name); + + static const char * + GetGroupName (uint32_t gid, std::string &group_name); + + static uint32_t + GetUserID (); + + static uint32_t + GetGroupID (); + + static uint32_t + GetEffectiveUserID (); + + static uint32_t + GetEffectiveGroupID (); + + + enum SystemLogType + { + eSystemLogWarning, + eSystemLogError + }; + + static void + SystemLog (SystemLogType type, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + static void + SystemLog (SystemLogType type, const char *format, va_list args); + + //------------------------------------------------------------------ + /// Gets the host architecture. + /// + /// @return + /// A const architecture object that represents the host + /// architecture. + //------------------------------------------------------------------ + enum SystemDefaultArchitecture + { + eSystemDefaultArchitecture, // The overall default architecture that applications will run on this host + eSystemDefaultArchitecture32, // If this host supports 32 bit programs, return the default 32 bit arch + eSystemDefaultArchitecture64 // If this host supports 64 bit programs, return the default 64 bit arch + }; + + static const ArchSpec & + GetArchitecture (SystemDefaultArchitecture arch_kind = eSystemDefaultArchitecture); + + //------------------------------------------------------------------ + /// Gets the host vendor string. + /// + /// @return + /// A const string object containing the host vendor name. + //------------------------------------------------------------------ + static const ConstString & + GetVendorString (); + + //------------------------------------------------------------------ + /// Gets the host Operating System (OS) string. + /// + /// @return + /// A const string object containing the host OS name. + //------------------------------------------------------------------ + static const ConstString & + GetOSString (); + + //------------------------------------------------------------------ + /// Gets the host target triple as a const string. + /// + /// @return + /// A const string object containing the host target triple. + //------------------------------------------------------------------ + static const ConstString & + GetTargetTriple (); + + //------------------------------------------------------------------ + /// Get the process ID for the calling process. + /// + /// @return + /// The process ID for the current process. + //------------------------------------------------------------------ + static lldb::pid_t + GetCurrentProcessID (); + + //------------------------------------------------------------------ + /// Get the thread ID for the calling thread in the current process. + /// + /// @return + /// The thread ID for the calling thread in the current process. + //------------------------------------------------------------------ + static lldb::tid_t + GetCurrentThreadID (); + + //------------------------------------------------------------------ + /// Get the thread token (the one returned by ThreadCreate when the thread was created) for the + /// calling thread in the current process. + /// + /// @return + /// The thread token for the calling thread in the current process. + //------------------------------------------------------------------ + static lldb::thread_t + GetCurrentThread (); + + static const char * + GetSignalAsCString (int signo); + + static void + WillTerminate (); + //------------------------------------------------------------------ + /// Host specific thread created function call. + /// + /// This function call lets the current host OS do any thread + /// specific initialization that it needs, including naming the + /// thread. No cleanup routine is exptected to be called + /// + /// @param[in] name + /// The current thread's name in the current process. + //------------------------------------------------------------------ + static void + ThreadCreated (const char *name); + + static lldb::thread_t + ThreadCreate (const char *name, + lldb::thread_func_t function, + lldb::thread_arg_t thread_arg, + Error *err); + + static bool + ThreadCancel (lldb::thread_t thread, + Error *error); + + static bool + ThreadDetach (lldb::thread_t thread, + Error *error); + static bool + ThreadJoin (lldb::thread_t thread, + lldb::thread_result_t *thread_result_ptr, + Error *error); + + //------------------------------------------------------------------ + /// Gets the name of a thread in a process. + /// + /// This function will name a thread in a process using it's own + /// thread name pool, and also will attempt to set a thread name + /// using any supported host OS APIs. + /// + /// @param[in] pid + /// The process ID in which we are trying to get the name of + /// a thread. + /// + /// @param[in] tid + /// The thread ID for which we are trying retrieve the name of. + /// + /// @return + /// A std::string containing the thread name. + //------------------------------------------------------------------ + static std::string + GetThreadName (lldb::pid_t pid, lldb::tid_t tid); + + //------------------------------------------------------------------ + /// Sets the name of a thread in the current process. + /// + /// @param[in] pid + /// The process ID in which we are trying to name a thread. + /// + /// @param[in] tid + /// The thread ID which we are trying to name. + /// + /// @param[in] name + /// The current thread's name in the current process to \a name. + /// + /// @return + /// \b true if the thread name was able to be set, \b false + /// otherwise. + //------------------------------------------------------------------ + static bool + SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name); + + //------------------------------------------------------------------ + /// Sets a shortened name of a thread in the current process. + /// + /// @param[in] pid + /// The process ID in which we are trying to name a thread. + /// + /// @param[in] tid + /// The thread ID which we are trying to name. + /// + /// @param[in] name + /// The current thread's name in the current process to \a name. + /// + /// @param[in] len + /// The maximum length for the thread's shortened name. + /// + /// @return + /// \b true if the thread name was able to be set, \b false + /// otherwise. + static bool + SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name, size_t len); + + //------------------------------------------------------------------ + /// Gets the FileSpec of the current process (the process that + /// that is running the LLDB code). + /// + /// @return + /// \b A file spec with the program name. + //------------------------------------------------------------------ + static FileSpec + GetProgramFileSpec (); + + //------------------------------------------------------------------ + /// Given an address in the current process (the process that + /// is running the LLDB code), return the name of the module that + /// it comes from. This can be useful when you need to know the + /// path to the shared library that your code is running in for + /// loading resources that are relative to your binary. + /// + /// @param[in] host_addr + /// The pointer to some code in the current process. + /// + /// @return + /// \b A file spec with the module that contains \a host_addr, + /// which may be invalid if \a host_addr doesn't fall into + /// any valid module address range. + //------------------------------------------------------------------ + static FileSpec + GetModuleFileSpecForHostAddress (const void *host_addr); + + + + //------------------------------------------------------------------ + /// If you have an executable that is in a bundle and want to get + /// back to the bundle directory from the path itself, this + /// function will change a path to a file within a bundle to the + /// bundle directory itself. + /// + /// @param[in] file + /// A file spec that might point to a file in a bundle. + /// + /// @param[out] bundle_directory + /// An object will be filled in with the bundle directory for + /// the bundle when \b true is returned. Otherwise \a file is + /// left untouched and \b false is returned. + /// + /// @return + /// \b true if \a file was resolved in \a bundle_directory, + /// \b false otherwise. + //------------------------------------------------------------------ + static bool + GetBundleDirectory (const FileSpec &file, FileSpec &bundle_directory); + + //------------------------------------------------------------------ + /// When executable files may live within a directory, where the + /// directory represents an executable bundle (like the MacOSX + /// app bundles), the locate the executable within the containing + /// bundle. + /// + /// @param[in,out] file + /// A file spec that currently points to the bundle that will + /// be filled in with the executable path within the bundle + /// if \b true is returned. Otherwise \a file is left untouched. + /// + /// @return + /// \b true if \a file was resolved, \b false if this function + /// was not able to resolve the path. + //------------------------------------------------------------------ + static bool + ResolveExecutableInBundle (FileSpec &file); + + //------------------------------------------------------------------ + /// Find a resource files that are related to LLDB. + /// + /// Operating systems have different ways of storing shared + /// libraries and related resources. This function abstracts the + /// access to these paths. + /// + /// @param[in] path_type + /// The type of LLDB resource path you are looking for. If the + /// enumeration ends with "Dir", then only the \a file_spec's + /// directory member gets filled in. + /// + /// @param[in] file_spec + /// A file spec that gets filled in with the appriopriate path. + /// + /// @return + /// \b true if \a resource_path was resolved, \a false otherwise. + //------------------------------------------------------------------ + static bool + GetLLDBPath (PathType path_type, + FileSpec &file_spec); + + //------------------------------------------------------------------ + /// Set a string that can be displayed if host application crashes. + /// + /// Some operating systems have the ability to print a description + /// for shared libraries when a program crashes. If the host OS + /// supports such a mechanism, it should be implemented to help + /// with crash triage. + /// + /// @param[in] format + /// A printf format that will be used to form a new crash + /// description string. + //------------------------------------------------------------------ + static void + SetCrashDescriptionWithFormat (const char *format, ...) __attribute__ ((format (printf, 1, 2))); + + static void + SetCrashDescription (const char *description); + + static uint32_t + FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &proc_infos); + + typedef std::map TidMap; + typedef std::pair TidPair; + static bool + FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach); + + static bool + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); + + static lldb::pid_t + LaunchApplication (const FileSpec &app_file_spec); + + static Error + LaunchProcess (ProcessLaunchInfo &launch_info); + + static Error + RunShellCommand (const char *command, // Shouldn't be NULL + const char *working_dir, // Pass NULL 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 + uint32_t timeout_sec, + const char *shell = "/bin/bash"); + + static lldb::DataBufferSP + GetAuxvData (lldb_private::Process *process); + + static lldb::TargetSP + GetDummyTarget (Debugger &debugger); + + static bool + OpenFileInExternalEditor (const FileSpec &file_spec, + uint32_t line_no); + + static void + Backtrace (Stream &strm, uint32_t max_frames); + + static size_t + GetEnvironment (StringList &env); + + enum DynamicLibraryOpenOptions + { + eDynamicLibraryOpenOptionLazy = (1u << 0), // Lazily resolve symbols in this dynamic library + eDynamicLibraryOpenOptionLocal = (1u << 1), // Only open a shared library with local access (hide it from the global symbol namespace) + eDynamicLibraryOpenOptionLimitGetSymbol = (1u << 2) // DynamicLibraryGetSymbol calls on this handle will only return matches from this shared library + }; + static void * + DynamicLibraryOpen (const FileSpec &file_spec, + uint32_t options, + Error &error); + + static Error + DynamicLibraryClose (void *dynamic_library_handle); + + static void * + DynamicLibraryGetSymbol (void *dynamic_library_handle, + const char *symbol_name, + Error &error); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // liblldb_Host_h_ diff --git a/include/lldb/Host/Mutex.h b/include/lldb/Host/Mutex.h new file mode 100644 index 00000000000..63f759efe36 --- /dev/null +++ b/include/lldb/Host/Mutex.h @@ -0,0 +1,312 @@ +//===-- Mutex.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Mutex_h_ +#define liblldb_Mutex_h_ +#if defined(__cplusplus) + +#include +#include + +#ifdef LLDB_CONFIGURATION_DEBUG +#include +#endif + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Mutex Mutex.h "lldb/Host/Mutex.h" +/// @brief A C++ wrapper class for pthread mutexes. +//---------------------------------------------------------------------- +class Mutex +{ +public: + friend class Locker; + friend class Condition; + + enum Type + { + eMutexTypeNormal, ///< Mutex that can't recursively entered by the same thread + eMutexTypeRecursive ///< Mutex can be recursively entered by the same thread + }; + + //------------------------------------------------------------------ + /// @class Mutex::Locker + /// + /// A scoped locking class that allows a variety of pthread mutex + /// objects to have a mutex locked when an Mutex::Locker + /// object is created, and unlocked when it goes out of scope or + /// when the Mutex::Locker::Reset(pthread_mutex_t *) + /// is called. This provides an exception safe way to lock a mutex + /// in a scope. + //------------------------------------------------------------------ + class Locker + { + public: + //-------------------------------------------------------------- + /// Default constructor. + /// + /// This will create a scoped mutex locking object that doesn't + /// have a mutex to lock. One will need to be provided using the + /// Mutex::Locker::Reset(pthread_mutex_t *) method. + /// + /// @see Mutex::Locker::Reset(pthread_mutex_t *) + //-------------------------------------------------------------- + Locker(); + + //-------------------------------------------------------------- + /// Constructor with a Mutex object. + /// + /// This will create a scoped mutex locking object that extracts + /// the mutex owned by \a m and locks it. + /// + /// @param[in] m + /// An instance of a Mutex object that contains a + /// valid mutex object. + //-------------------------------------------------------------- + Locker(Mutex& m); + + //-------------------------------------------------------------- + /// Constructor with a Mutex object pointer. + /// + /// This will create a scoped mutex locking object that extracts + /// the mutex owned by a m and locks it. + /// + /// @param[in] m + /// A pointer to instance of a Mutex object that + /// contains a valid mutex object. + //-------------------------------------------------------------- + Locker(Mutex* m); + + //-------------------------------------------------------------- + /// Desstructor + /// + /// Unlocks any valid pthread_mutex_t that this object may + /// contain. + //-------------------------------------------------------------- + ~Locker(); + + //-------------------------------------------------------------- + /// Change the contained mutex. + /// + /// Unlock the current mutex in this object (if it contains a + /// valid mutex) and lock the new \a mutex object if it is + /// non-NULL. + //-------------------------------------------------------------- + void + Lock (Mutex &mutex); + + void + Lock (Mutex *mutex) + { + if (mutex) + Lock(*mutex); + } + + //-------------------------------------------------------------- + /// Change the contained mutex only if the mutex can be locked. + /// + /// Unlock the current mutex in this object (if it contains a + /// valid mutex) and try to lock \a mutex. If \a mutex can be + /// locked this object will take ownership of the lock and will + /// unlock it when it goes out of scope or Reset or TryLock are + /// called again. If the mutex is already locked, this object + /// will not take ownership of the mutex. + /// + /// @return + /// Returns \b true if the lock was aquired and the this + /// object will unlock the mutex when it goes out of scope, + /// returns \b false otherwise. + //-------------------------------------------------------------- + bool + TryLock (Mutex &mutex, const char *failure_message = NULL); + + bool + TryLock (Mutex *mutex, const char *failure_message = NULL) + { + if (mutex) + return TryLock(*mutex, failure_message); + else + return false; + } + + void + Unlock (); + + protected: + //-------------------------------------------------------------- + /// Member variables + //-------------------------------------------------------------- + Mutex *m_mutex_ptr; + + private: + Locker(const Locker&); + const Locker& operator=(const Locker&); + }; + + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Creates a pthread mutex with no attributes. + //------------------------------------------------------------------ + Mutex(); + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Creates a pthread mutex with \a type as the mutex type. + /// Valid values for \a type include: + /// @li Mutex::Type::eMutexTypeNormal + /// @li Mutex::Type::eMutexTypeRecursive + /// + /// @param[in] type + /// The type of the mutex. + /// + /// @see ::pthread_mutexattr_settype() + //------------------------------------------------------------------ + Mutex(Mutex::Type type); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Destroys the mutex owned by this object. + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + ~Mutex(); + + //------------------------------------------------------------------ + /// Lock the mutex. + /// + /// Locks the mutex owned by this object. If the mutex is already + /// locked, the calling thread will block until the mutex becomes + /// available. + /// + /// @return + /// The error code from \c pthread_mutex_lock(). + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + int + Lock(); + + //------------------------------------------------------------------ + /// Try to lock the mutex. + /// + /// Attempts to lock the mutex owned by this object without blocking. + /// If the mutex is already locked, TryLock() will not block waiting + /// for the mutex, but will return an error condition. + /// + /// @return + /// The error code from \c pthread_mutex_trylock(). + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + int + TryLock(const char *failure_message = NULL); + + //------------------------------------------------------------------ + /// Unlock the mutex. + /// + /// If the current thread holds the lock on the owned mutex, then + /// Unlock() will unlock the mutex. Calling Unlock() on this object + /// when the calling thread does not hold the lock will result in + /// undefined behavior. + /// + /// @return + /// The error code from \c pthread_mutex_unlock(). + //------------------------------------------------------------------ +#ifdef LLDB_CONFIGURATION_DEBUG + virtual +#endif + int + Unlock(); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + // TODO: Hide the mutex in the implementation file in case we ever need to port to an + // architecture that doesn't have pthread mutexes. + pthread_mutex_t m_mutex; ///< The pthread mutex object. + +private: + //------------------------------------------------------------------ + /// Mutex get accessor. + /// + /// @return + /// A pointer to the pthread mutex object owned by this object. + //------------------------------------------------------------------ + pthread_mutex_t * + GetMutex(); + + Mutex(const Mutex&); + const Mutex& operator=(const Mutex&); +}; + +#ifdef LLDB_CONFIGURATION_DEBUG +class TrackingMutex : public Mutex +{ +public: + TrackingMutex() : Mutex() {} + TrackingMutex(Mutex::Type type) : Mutex (type) {} + + virtual + ~TrackingMutex() {} + + virtual int + Unlock (); + + virtual int + TryLock (const char *failure_message = NULL) + { + int return_value = Mutex::TryLock(); + if (return_value != 0 && failure_message != NULL) + { + m_failure_message.assign(failure_message); + m_thread_that_tried = pthread_self(); + } + return return_value; + } + +protected: + pthread_t m_thread_that_tried; + std::string m_failure_message; +}; + +class LoggingMutex : public Mutex +{ +public: + LoggingMutex() : Mutex(),m_locked(false) {} + LoggingMutex(Mutex::Type type) : Mutex (type),m_locked(false) {} + + virtual + ~LoggingMutex() {} + + virtual int + Lock (); + + virtual int + Unlock (); + + virtual int + TryLock (const char *failure_message = NULL); +protected: + bool m_locked; +}; +#endif + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif diff --git a/include/lldb/Host/Predicate.h b/include/lldb/Host/Predicate.h new file mode 100644 index 00000000000..6ddf20b67c6 --- /dev/null +++ b/include/lldb/Host/Predicate.h @@ -0,0 +1,509 @@ +//===-- Predicate.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Predicate_h_ +#define liblldb_Predicate_h_ +#if defined(__cplusplus) + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Condition.h" +#include +#include + +//#define DB_PTHREAD_LOG_EVENTS + +//---------------------------------------------------------------------- +/// Enumerations for broadcasting. +//---------------------------------------------------------------------- +namespace lldb_private { + +typedef enum +{ + eBroadcastNever, ///< No broadcast will be sent when the value is modified. + eBroadcastAlways, ///< Always send a broadcast when the value is modified. + eBroadcastOnChange ///< Only broadcast if the value changes when the value is modified. + +} PredicateBroadcastType; + +//---------------------------------------------------------------------- +/// @class Predicate Predicate.h "lldb/Host/Predicate.h" +/// @brief A C++ wrapper class for providing threaded access to a value +/// of type T. +/// +/// A templatized class that provides multi-threaded access to a value +/// of type T. Threads can efficiently wait for bits within T to be set +/// or reset, or wait for T to be set to be equal/not equal to a +/// specified values. +//---------------------------------------------------------------------- +template +class Predicate +{ +public: + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initializes the mutex, condition and value with their default + /// constructors. + //------------------------------------------------------------------ + Predicate () : + m_value(), + m_mutex(), + m_condition() + { + } + + //------------------------------------------------------------------ + /// Construct with initial T value \a initial_value. + /// + /// Initializes the mutex and condition with their default + /// constructors, and initializes the value with \a initial_value. + /// + /// @param[in] initial_value + /// The initial value for our T object. + //------------------------------------------------------------------ + Predicate (T initial_value) : + m_value(initial_value), + m_mutex(), + m_condition() + { + } + + //------------------------------------------------------------------ + /// Destructor. + /// + /// Destrory the condition, mutex, and T objects. + //------------------------------------------------------------------ + ~Predicate () + { + } + + + //------------------------------------------------------------------ + /// Value get accessor. + /// + /// Copies the current \a m_value in a thread safe manor and returns + /// the copied value. + /// + /// @return + /// A copy of the current value. + //------------------------------------------------------------------ + T + GetValue () const + { + Mutex::Locker locker(m_mutex); + T value = m_value; + return value; + } + + //------------------------------------------------------------------ + /// Value set accessor. + /// + /// Set the contained \a m_value to \a new_value in a thread safe + /// way and broadcast if needed. + /// + /// @param[in] value + /// The new value to set. + /// + /// @param[in] broadcast_type + /// A value indicating when and if to broadast. See the + /// PredicateBroadcastType enumeration for details. + /// + /// @see Predicate::Broadcast() + //------------------------------------------------------------------ + void + SetValue (T value, PredicateBroadcastType broadcast_type) + { + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (value = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, value, broadcast_type); +#endif + const T old_value = m_value; + m_value = value; + + Broadcast(old_value, broadcast_type); + } + + //------------------------------------------------------------------ + /// Set some bits in \a m_value. + /// + /// Logically set the bits \a bits in the contained \a m_value in a + /// thread safe way and broadcast if needed. + /// + /// @param[in] bits + /// The bits to set in \a m_value. + /// + /// @param[in] broadcast_type + /// A value indicating when and if to broadast. See the + /// PredicateBroadcastType enumeration for details. + /// + /// @see Predicate::Broadcast() + //------------------------------------------------------------------ + void + SetValueBits (T bits, PredicateBroadcastType broadcast_type) + { + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, bits, broadcast_type); +#endif + const T old_value = m_value; + m_value |= bits; + + Broadcast(old_value, broadcast_type); + } + + //------------------------------------------------------------------ + /// Reset some bits in \a m_value. + /// + /// Logically reset (clear) the bits \a bits in the contained + /// \a m_value in a thread safe way and broadcast if needed. + /// + /// @param[in] bits + /// The bits to clear in \a m_value. + /// + /// @param[in] broadcast_type + /// A value indicating when and if to broadast. See the + /// PredicateBroadcastType enumeration for details. + /// + /// @see Predicate::Broadcast() + //------------------------------------------------------------------ + void + ResetValueBits (T bits, PredicateBroadcastType broadcast_type) + { + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, bits, broadcast_type); +#endif + const T old_value = m_value; + m_value &= ~bits; + + Broadcast(old_value, broadcast_type); + } + + //------------------------------------------------------------------ + /// Wait for bits to be set in \a m_value. + /// + /// Waits in a thread safe way for any bits in \a bits to get + /// logically set in \a m_value. If any bits are already set in + /// \a m_value, this function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the bits are set and the time the waiting thread wakes up. + /// If the bits are no longer set when the waiting thread wakes + /// up, it will go back into a wait state. It may be necessary + /// for the calling code to use additional thread synchronization + /// methods to detect transitory states. + /// + /// @param[in] bits + /// The bits we are waiting to be set in \a m_value. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @return + /// Any bits of the requested bits that actually were set within + /// the time specified. Zero if a timeout or unrecoverable error + /// occurred. + //------------------------------------------------------------------ + T + WaitForSetValueBits (T bits, const TimeValue *abstime = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, bits, abstime, m_value); +#endif + while (err == 0 && ((m_value & bits) == 0)) + { + err = m_condition.Wait (m_mutex, abstime); + } +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x\n", __FUNCTION__, bits, m_value, m_value & bits); +#endif + + return m_value & bits; + } + + //------------------------------------------------------------------ + /// Wait for bits to be reset in \a m_value. + /// + /// Waits in a thread safe way for any bits in \a bits to get + /// logically reset in \a m_value. If all bits are already reset in + /// \a m_value, this function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the bits are reset and the time the waiting thread wakes up. + /// If the bits are no set when the waiting thread wakes up, it will + /// go back into a wait state. It may be necessary for the calling + /// code to use additional thread synchronization methods to detect + /// transitory states. + /// + /// @param[in] bits + /// The bits we are waiting to be reset in \a m_value. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @return + /// Zero on successful waits, or non-zero if a timeout or + /// unrecoverable error occurs. + //------------------------------------------------------------------ + T + WaitForResetValueBits (T bits, const TimeValue *abstime = NULL) + { + int err = 0; + + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, bits, abstime, m_value); +#endif + while (err == 0 && ((m_value & bits) != 0)) + { + err = m_condition.Wait (m_mutex, abstime); + } + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x\n", __FUNCTION__, bits, m_value, m_value & bits); +#endif + return m_value & bits; + } + + //------------------------------------------------------------------ + /// Wait for \a m_value to be equal to \a value. + /// + /// Waits in a thread safe way for \a m_value to be equal to \a + /// value. If \a m_value is already equal to \a value, this + /// function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the value is set and the time the waiting thread wakes up. + /// If the value no longer matches the requested value when the + /// waiting thread wakes up, it will go back into a wait state. It + /// may be necessary for the calling code to use additional thread + /// synchronization methods to detect transitory states. + /// + /// @param[in] value + /// The value we want \a m_value to be equal to. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @param[out] timed_out + /// If not null, set to true if we return because of a time out, + /// and false if the value was set. + /// + /// @return + /// @li \b true if the \a m_value is equal to \a value + /// @li \b false otherwise + //------------------------------------------------------------------ + bool + WaitForValueEqualTo (T value, const TimeValue *abstime = NULL, bool *timed_out = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, value, abstime, m_value); +#endif + if (timed_out) + *timed_out = false; + + while (err == 0 && m_value != value) + { + err = m_condition.Wait (m_mutex, abstime, timed_out); + } + + return m_value == value; + } + + //------------------------------------------------------------------ + /// Wait for \a m_value to be equal to \a value and then set it to + /// a new value. + /// + /// Waits in a thread safe way for \a m_value to be equal to \a + /// value and then sets \a m_value to \a new_value. If \a m_value + /// is already equal to \a value, this function will immediately + /// set \a m_value to \a new_value and return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the value is set and the time the waiting thread wakes up. + /// If the value no longer matches the requested value when the + /// waiting thread wakes up, it will go back into a wait state. It + /// may be necessary for the calling code to use additional thread + /// synchronization methods to detect transitory states. + /// + /// @param[in] value + /// The value we want \a m_value to be equal to. + /// + /// @param[in] new_value + /// The value to which \a m_value will be set if \b true is + /// returned. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @param[out] timed_out + /// If not null, set to true if we return because of a time out, + /// and false if the value was set. + /// + /// @return + /// @li \b true if the \a m_value became equal to \a value + /// @li \b false otherwise + //------------------------------------------------------------------ + bool + WaitForValueEqualToAndSetValueTo (T wait_value, T new_value, const TimeValue *abstime = NULL, bool *timed_out = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); + +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (wait_value = 0x%8.8x, new_value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, wait_value, new_value, abstime, m_value); +#endif + if (timed_out) + *timed_out = false; + + while (err == 0 && m_value != wait_value) + { + err = m_condition.Wait (m_mutex, abstime, timed_out); + } + + if (m_value == wait_value) + { + m_value = new_value; + return true; + } + + return false; + } + + + //------------------------------------------------------------------ + /// Wait for \a m_value to not be equal to \a value. + /// + /// Waits in a thread safe way for \a m_value to not be equal to \a + /// value. If \a m_value is already not equal to \a value, this + /// function will return without waiting. + /// + /// It is possible for the value to be changed between the time + /// the value is set and the time the waiting thread wakes up. + /// If the value is equal to the test value when the waiting thread + /// wakes up, it will go back into a wait state. It may be + /// necessary for the calling code to use additional thread + /// synchronization methods to detect transitory states. + /// + /// @param[in] value + /// The value we want \a m_value to not be equal to. + /// + /// @param[out] new_value + /// The new value if \b true is returned. + /// + /// @param[in] abstime + /// If non-NULL, the absolute time at which we should stop + /// waiting, else wait an infinite amount of time. + /// + /// @return + /// @li \b true if the \a m_value is equal to \a value + /// @li \b false otherwise + //------------------------------------------------------------------ + bool + WaitForValueNotEqualTo (T value, T &new_value, const TimeValue *abstime = NULL) + { + int err = 0; + // pthread_cond_timedwait() or pthread_cond_wait() will atomically + // unlock the mutex and wait for the condition to be set. When either + // function returns, they will re-lock the mutex. We use an auto lock/unlock + // class (Mutex::Locker) to allow us to return at any point in this + // function and not have to worry about unlocking the mutex. + Mutex::Locker locker(m_mutex); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, value, abstime, m_value); +#endif + while (err == 0 && m_value == value) + { + err = m_condition.Wait (m_mutex, abstime); + } + + if (m_value != value) + { + new_value = m_value; + return true; + } + return false; + } + +protected: + //---------------------------------------------------------------------- + // pthread condition and mutex variable to controll access and allow + // blocking between the main thread and the spotlight index thread. + //---------------------------------------------------------------------- + T m_value; ///< The templatized value T that we are protecting access to + mutable Mutex m_mutex; ///< The mutex to use when accessing the data + Condition m_condition; ///< The pthread condition variable to use for signaling that data available or changed. + +private: + + //------------------------------------------------------------------ + /// Broadcast if needed. + /// + /// Check to see if we need to broadcast to our condition variable + /// depedning on the \a old_value and on the \a broadcast_type. + /// + /// If \a broadcast_type is eBroadcastNever, no broadcast will be + /// sent. + /// + /// If \a broadcast_type is eBroadcastAlways, the condition variable + /// will always be broadcast. + /// + /// If \a broadcast_type is eBroadcastOnChange, the condition + /// variable be broadcast if the owned value changes. + //------------------------------------------------------------------ + void + Broadcast (T old_value, PredicateBroadcastType broadcast_type) + { + bool broadcast = (broadcast_type == eBroadcastAlways) || ((broadcast_type == eBroadcastOnChange) && old_value != m_value); +#ifdef DB_PTHREAD_LOG_EVENTS + printf("%s (old_value = 0x%8.8x, broadcast_type = %i) m_value = 0x%8.8x, broadcast = %u\n", __FUNCTION__, old_value, broadcast_type, m_value, broadcast); +#endif + if (broadcast) + m_condition.Broadcast(); + } + + + DISALLOW_COPY_AND_ASSIGN(Predicate); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Predicate_h_ diff --git a/include/lldb/Host/ProcessRunLock.h b/include/lldb/Host/ProcessRunLock.h new file mode 100644 index 00000000000..f563be73fce --- /dev/null +++ b/include/lldb/Host/ProcessRunLock.h @@ -0,0 +1,165 @@ +//===-- ProcessRunLock.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ProcessRunLock_h_ +#define liblldb_ProcessRunLock_h_ +#if defined(__cplusplus) + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Condition.h" +#include +#include +#include + +//---------------------------------------------------------------------- +/// Enumerations for broadcasting. +//---------------------------------------------------------------------- +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ProcessRunLock ProcessRunLock.h "lldb/Host/ProcessRunLock.h" +/// @brief A class used to prevent the process from starting while other +/// threads are accessing its data, and prevent access to its data while +/// it is running. +//---------------------------------------------------------------------- + +class ProcessRunLock +{ +public: + ProcessRunLock () : + m_rwlock(), + m_running(false) + { + int err = ::pthread_rwlock_init(&m_rwlock, NULL); (void)err; +//#if LLDB_CONFIGURATION_DEBUG +// assert(err == 0); +//#endif + } + + ~ProcessRunLock () + { + int err = ::pthread_rwlock_destroy (&m_rwlock); (void)err; +//#if LLDB_CONFIGURATION_DEBUG +// assert(err == 0); +//#endif + } + + bool + ReadTryLock () + { + ::pthread_rwlock_rdlock (&m_rwlock); + if (m_running == false) + { + return true; + } + ::pthread_rwlock_unlock (&m_rwlock); + return false; + } + + bool + ReadUnlock () + { + return ::pthread_rwlock_unlock (&m_rwlock) == 0; + } + + bool + SetRunning() + { + ::pthread_rwlock_wrlock (&m_rwlock); + m_running = true; + ::pthread_rwlock_unlock (&m_rwlock); + return true; + } + + bool + 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 + SetStopped () + { + ::pthread_rwlock_wrlock (&m_rwlock); + m_running = false; + ::pthread_rwlock_unlock (&m_rwlock); + return true; + } + + class ProcessRunLocker + { + public: + ProcessRunLocker () : + m_lock (NULL) + { + } + + ~ProcessRunLocker() + { + Unlock(); + } + + // Try to lock the read lock, but only do so if there are no writers. + bool + TryLock (ProcessRunLock *lock) + { + if (m_lock) + { + if (m_lock == lock) + return true; // We already have this lock locked + else + Unlock(); + } + if (lock) + { + if (lock->ReadTryLock()) + { + m_lock = lock; + return true; + } + } + return false; + } + + protected: + void + Unlock () + { + if (m_lock) + { + m_lock->ReadUnlock(); + m_lock = NULL; + } + } + + ProcessRunLock *m_lock; + private: + DISALLOW_COPY_AND_ASSIGN(ProcessRunLocker); + }; + +protected: + pthread_rwlock_t m_rwlock; + bool m_running; +private: + DISALLOW_COPY_AND_ASSIGN(ProcessRunLock); +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_ProcessRunLock_h_ diff --git a/include/lldb/Host/SocketAddress.h b/include/lldb/Host/SocketAddress.h new file mode 100644 index 00000000000..e63b238c799 --- /dev/null +++ b/include/lldb/Host/SocketAddress.h @@ -0,0 +1,256 @@ +//===-- SocketAddress.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SocketAddress_h_ +#define liblldb_SocketAddress_h_ + +// C Includes +#include +#include +#include +#include + +#if defined(__FreeBSD__) +#include +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes + +namespace lldb_private { + +class SocketAddress +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SocketAddress (); + SocketAddress (const struct sockaddr &s); + SocketAddress (const struct sockaddr_in &s); + SocketAddress (const struct sockaddr_in6 &s); + SocketAddress (const struct sockaddr_storage &s); + SocketAddress (const SocketAddress& rhs); + ~SocketAddress (); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const SocketAddress& + operator=(const SocketAddress& rhs); + + const SocketAddress& + operator=(const struct addrinfo *addr_info); + + const SocketAddress& + operator=(const struct sockaddr &s); + + const SocketAddress& + operator=(const struct sockaddr_in &s); + + const SocketAddress& + operator=(const struct sockaddr_in6 &s); + + const SocketAddress& + operator=(const struct sockaddr_storage &s); + + //------------------------------------------------------------------ + // Clear the contents of this socket address + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + // Get the length for the current socket address family + //------------------------------------------------------------------ + socklen_t + GetLength () const; + + //------------------------------------------------------------------ + // Get the mex length for the the largest socket address supported. + //------------------------------------------------------------------ + static socklen_t + GetMaxLength (); + + //------------------------------------------------------------------ + // Get the socket address family + //------------------------------------------------------------------ + sa_family_t + GetFamily () const; + + //------------------------------------------------------------------ + // Set the socket address family + //------------------------------------------------------------------ + void + SetFamily (sa_family_t family); + + //------------------------------------------------------------------ + // Get the port if the socket address for the family has a port + //------------------------------------------------------------------ + in_port_t + GetPort () const; + + //------------------------------------------------------------------ + // Set the port if the socket address for the family has a port. + // The family must be set correctly prior to calling this function. + //------------------------------------------------------------------ + bool + SetPort (in_port_t port); + + //------------------------------------------------------------------ + // Set the socket address according to the first match from a call + // to getaddrinfo() (or equivalent functions for systems that don't + // have getaddrinfo(). If "addr_info_ptr" is not NULL, it will get + // filled in with the match that was used to populate this socket + // address. + //------------------------------------------------------------------ + bool + SetAddress (const struct addrinfo *hints_ptr, // Optional hints where the family, protocol and other things can be specified. + const char *host, // Hostname ("foo.bar.com" or "foo" or IP address string ("123.234.12.1" or "2001:0db8:85a3:0000:0000:8a2e:0370:7334") + const char *service, // Protocol name ("tcp", "http", etc) or a raw port number string ("81") + struct addrinfo *addr_info_ptr); // If non-NULL, this will get filled in with the match + + //------------------------------------------------------------------ + // Quick way to set the SocketAddress to localhost given the family. + // Returns true if successful, false if "family" doesn't support + // localhost or if "family" is not supported by this class. + //------------------------------------------------------------------ + bool + SetToLocalhost (sa_family_t family, + in_port_t port); + + //------------------------------------------------------------------ + // Returns true if there is a valid socket address in this object. + //------------------------------------------------------------------ + bool + IsValid () const; + + //------------------------------------------------------------------ + // Direct access to all of the sockaddr structures + //------------------------------------------------------------------ + struct sockaddr & + sockaddr () + { + return m_socket_addr.sa; + } + + const struct sockaddr & + sockaddr () const + { + return m_socket_addr.sa; + } + + struct sockaddr_in & + sockaddr_in () + { + return m_socket_addr.sa_ipv4; + } + + const struct sockaddr_in & + sockaddr_in () const + { + return m_socket_addr.sa_ipv4; + } + + struct sockaddr_in6 & + sockaddr_in6 () + { + return m_socket_addr.sa_ipv6; + } + + const struct sockaddr_in6 & + sockaddr_in6 () const + { + return m_socket_addr.sa_ipv6; + } + + struct sockaddr_storage & + sockaddr_storage () + { + return m_socket_addr.sa_storage; + } + + + const struct sockaddr_storage & + sockaddr_storage () const + { + return m_socket_addr.sa_storage; + } + + + //------------------------------------------------------------------ + // Conversion operators to allow getting the contents of this class + // as a pointer to the appropriate structure. This allows an instance + // of this class to be used in calls that take one of the sockaddr + // structure variants without having to manally use the correct + // accessor function. + //------------------------------------------------------------------ + + operator struct sockaddr * () + { + return &m_socket_addr.sa; + } + + operator const struct sockaddr * () const + { + return &m_socket_addr.sa; + } + + operator struct sockaddr_in * () + { + return &m_socket_addr.sa_ipv4; + } + + operator const struct sockaddr_in * () const + { + return &m_socket_addr.sa_ipv4; + } + + operator struct sockaddr_in6 * () + { + return &m_socket_addr.sa_ipv6; + } + + operator const struct sockaddr_in6 * () const + { + return &m_socket_addr.sa_ipv6; + } + + operator const struct sockaddr_storage * () const + { + return &m_socket_addr.sa_storage; + } + + operator struct sockaddr_storage * () + { + return &m_socket_addr.sa_storage; + } + + +protected: + typedef union sockaddr_tag + { + struct sockaddr sa; + struct sockaddr_in sa_ipv4; + struct sockaddr_in6 sa_ipv6; + struct sockaddr_storage sa_storage; + } sockaddr_t; + + //------------------------------------------------------------------ + // Classes that inherit from SocketAddress can see and modify these + //------------------------------------------------------------------ + sockaddr_t m_socket_addr; +}; + + +} // namespace lldb_private + + +#endif // liblldb_SocketAddress_h_ diff --git a/include/lldb/Host/Symbols.h b/include/lldb/Host/Symbols.h new file mode 100644 index 00000000000..9db68e1ecf1 --- /dev/null +++ b/include/lldb/Host/Symbols.h @@ -0,0 +1,69 @@ +//===-- Symbols.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Symbols_h_ +#define liblldb_Symbols_h_ + +// C Includes +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +class Symbols +{ +public: + //---------------------------------------------------------------------- + // Locate the executable file given a module specification. + // + // Locating the file should happen only on the local computer or using + // the current computers global settings. + //---------------------------------------------------------------------- + static FileSpec + LocateExecutableObjectFile (const ModuleSpec &module_spec); + + //---------------------------------------------------------------------- + // Locate the symbol file given a module specification. + // + // Locating the file should happen only on the local computer or using + // the current computers global settings. + //---------------------------------------------------------------------- + static FileSpec + LocateExecutableSymbolFile (const ModuleSpec &module_spec); + + static FileSpec + FindSymbolFileInBundle (const FileSpec& dsym_bundle_fspec, + const lldb_private::UUID *uuid, + const ArchSpec *arch); + + //---------------------------------------------------------------------- + // Locate the object and symbol file given a module specification. + // + // Locating the file can try to download the file from a corporate build + // respository, or using any other means necessary to locate both the + // unstripped object file and the debug symbols. + // The force_lookup argument controls whether the external program is called + // unconditionally to find the symbol file, or if the user's settings are + // checked to see if they've enabled the external program before calling. + // + //---------------------------------------------------------------------- + static bool + DownloadObjectAndSymbolFile (ModuleSpec &module_spec, bool force_lookup = true); + +}; + +} // namespace lldb_private + + +#endif // liblldb_Symbols_h_ diff --git a/include/lldb/Host/Terminal.h b/include/lldb/Host/Terminal.h new file mode 100644 index 00000000000..b334717c796 --- /dev/null +++ b/include/lldb/Host/Terminal.h @@ -0,0 +1,254 @@ +//===-- Terminal.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Terminal_h_ +#define liblldb_Terminal_h_ +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +struct termios; + +namespace lldb_private { + +class Terminal +{ +public: + + Terminal (int fd = -1) : + m_fd (fd) + { + } + + ~Terminal () + { + } + + bool + IsATerminal () const; + + int + GetFileDescriptor () const + { + return m_fd; + } + + void + SetFileDescriptor (int fd) + { + m_fd = fd; + } + + bool + FileDescriptorIsValid () const + { + return m_fd != -1; + } + + void + Clear () + { + m_fd = -1; + } + + bool + SetEcho (bool enabled); + + bool + SetCanonical (bool enabled); + +protected: + int m_fd; // This may or may not be a terminal file descriptor +}; + + +//---------------------------------------------------------------------- +/// @class State Terminal.h "lldb/Host/Terminal.h" +/// @brief A terminal state saving/restoring class. +/// +/// This class can be used to remember the terminal state for a file +/// descriptor and later restore that state as it originally was. +//---------------------------------------------------------------------- +class TerminalState +{ +public: + //------------------------------------------------------------------ + /// Default constructor + //------------------------------------------------------------------ + TerminalState(); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~TerminalState(); + + //------------------------------------------------------------------ + /// Save the TTY state for \a fd. + /// + /// 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. + /// + /// @param[in] fd + /// The file descriptor to save the state of. + /// + /// @param[in] save_process_group + /// If \b true, save the process group settings, else do not + /// save the process group setttings for a TTY. + /// + /// @return + /// Returns \b true if \a fd describes a TTY and if the state + /// was able to be saved, \b false otherwise. + //------------------------------------------------------------------ + bool + Save (int fd, bool save_process_group); + + //------------------------------------------------------------------ + /// Restore the TTY state to the cached state. + /// + /// Restore the state of the TTY using the cached values from a + /// previous call to TerminalState::Save(int,bool). + /// + /// @return + /// Returns \b true if the TTY state was successfully restored, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + Restore () const; + + //------------------------------------------------------------------ + /// Test for valid cached TTY state information. + /// + /// @return + /// Returns \b true if this object has valid saved TTY state + /// settings that can be used to restore a previous state, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + IsValid() const; + + void + Clear (); + +protected: + + //------------------------------------------------------------------ + /// Test if tflags is valid. + /// + /// @return + /// Returns \b true if \a m_tflags is valid and can be restored, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + TFlagsIsValid() const; + + //------------------------------------------------------------------ + /// Test if ttystate is valid. + /// + /// @return + /// Returns \b true if \a m_ttystate is valid and can be + /// restored, \b false otherwise. + //------------------------------------------------------------------ + bool + TTYStateIsValid() const; + + //------------------------------------------------------------------ + /// Test if the process group information is valid. + /// + /// @return + /// Returns \b true if \a m_process_group is valid and can be + /// restored, \b false otherwise. + //------------------------------------------------------------------ + bool + ProcessGroupIsValid() const; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Terminal m_tty; ///< A terminal + int m_tflags; ///< Cached tflags information. + std::unique_ptr m_termios_ap; ///< Cached terminal state information. + lldb::pid_t m_process_group;///< Cached process group information. + +}; + +//---------------------------------------------------------------------- +/// @class TerminalStateSwitcher Terminal.h "lldb/Host/Terminal.h" +/// @brief A TTY state switching class. +/// +/// This class can be used to remember 2 TTY states for a given file +/// descriptor and switch between the two states. +//---------------------------------------------------------------------- +class TerminalStateSwitcher +{ +public: + //------------------------------------------------------------------ + /// Constructor + //------------------------------------------------------------------ + TerminalStateSwitcher(); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + ~TerminalStateSwitcher(); + + //------------------------------------------------------------------ + /// Get the number of possible states to save. + /// + /// @return + /// The number of states that this TTY switcher object contains. + //------------------------------------------------------------------ + uint32_t + GetNumberOfStates() const; + + //------------------------------------------------------------------ + /// Restore the TTY state for state at index \a idx. + /// + /// @return + /// Returns \b true if the TTY state was successfully restored, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + Restore (uint32_t idx) const; + + //------------------------------------------------------------------ + /// Save the TTY state information for the state at index \a idx. + /// The TTY state is saved for the file descriptor \a fd and + /// the process group information will also be saved if requested + /// by \a save_process_group. + /// + /// @param[in] idx + /// The index into the state array where the state should be + /// saved. + /// + /// @param[in] fd + /// The file descriptor for which to save the settings. + /// + /// @param[in] save_process_group + /// If \b true, save the process group information for the TTY. + /// + /// @return + /// Returns \b true if the save was successful, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + Save (uint32_t idx, int fd, bool save_process_group); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + mutable uint32_t m_currentState; ///< The currently active TTY state index. + TerminalState m_ttystates[2]; ///< The array of TTY states that holds saved TTY info. +}; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_Terminal_h_ diff --git a/include/lldb/Host/TimeValue.h b/include/lldb/Host/TimeValue.h new file mode 100644 index 00000000000..8c43d6d0e5f --- /dev/null +++ b/include/lldb/Host/TimeValue.h @@ -0,0 +1,107 @@ +//===-- TimeValue.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TimeValue_h_ +#define liblldb_TimeValue_h_ + +// C Includes +#include +#include + +// BEGIN: MinGW work around +#if !defined(_STRUCT_TIMESPEC) && !defined(HAVE_STRUCT_TIMESPEC) +#include +#endif +// END: MinGW work around + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class TimeValue +{ +public: + static const uint64_t MicroSecPerSec = 1000000UL; + static const uint64_t NanoSecPerSec = 1000000000UL; + static const uint64_t NanoSecPerMicroSec = 1000U; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TimeValue(); + TimeValue(const TimeValue& rhs); + TimeValue(const struct timespec& ts); + TimeValue(const struct timeval& tv); + ~TimeValue(); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const TimeValue& + operator=(const TimeValue& rhs); + + void + Clear (); + + uint64_t + GetAsNanoSecondsSinceJan1_1970() const; + + uint64_t + GetAsMicroSecondsSinceJan1_1970() const; + + uint64_t + GetAsSecondsSinceJan1_1970() const; + + struct timespec + GetAsTimeSpec () const; + + struct timeval + GetAsTimeVal () const; + + bool + IsValid () const; + + void + OffsetWithSeconds (uint64_t sec); + + void + OffsetWithMicroSeconds (uint64_t usec); + + void + OffsetWithNanoSeconds (uint64_t nsec); + + static TimeValue + Now(); + + void + Dump (Stream *s, uint32_t width = 0) const; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from TimeValue can see and modify these + //------------------------------------------------------------------ + uint64_t m_nano_seconds; +}; + +bool operator == (const TimeValue &lhs, const TimeValue &rhs); +bool operator != (const TimeValue &lhs, const TimeValue &rhs); +bool operator < (const TimeValue &lhs, const TimeValue &rhs); +bool operator <= (const TimeValue &lhs, const TimeValue &rhs); +bool operator > (const TimeValue &lhs, const TimeValue &rhs); +bool operator >= (const TimeValue &lhs, const TimeValue &rhs); + +uint64_t operator -(const TimeValue &lhs, const TimeValue &rhs); + +} // namespace lldb_private + + +#endif // liblldb_TimeValue_h_ diff --git a/include/lldb/Host/freebsd/Config.h b/include/lldb/Host/freebsd/Config.h new file mode 100644 index 00000000000..49d97dd5793 --- /dev/null +++ b/include/lldb/Host/freebsd/Config.h @@ -0,0 +1,28 @@ +//===-- Config.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +//---------------------------------------------------------------------- +// LLDB currently doesn't have a dynamic configuration mechanism, so we +// are going to hardcode things for now. Eventually these files will +// be auto generated by some configuration script that can detect +// platform functionality availability. +//---------------------------------------------------------------------- + +#ifndef liblldb_Platform_Config_h_ +#define liblldb_Platform_Config_h_ + +#define LLDB_CONFIG_TERMIOS_SUPPORTED 1 + +#define LLDB_CONFIG_TILDE_RESOLVES_TO_USER 1 + +//#define LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED 1 + +//#define LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED 1 + +#endif // #ifndef liblldb_Platform_Config_h_ diff --git a/include/lldb/Interpreter/Args.h b/include/lldb/Interpreter/Args.h new file mode 100644 index 00000000000..d06c3e56aec --- /dev/null +++ b/include/lldb/Interpreter/Args.h @@ -0,0 +1,467 @@ +//===-- Args.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Command_h_ +#define liblldb_Command_h_ + +// C Includes +#include + +// C++ Includes +#include +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-types.h" +#include "lldb/lldb-types.h" +#include "lldb/Core/Error.h" + +namespace lldb_private { + +typedef std::pair OptionArgValue; +typedef std::pair OptionArgPair; +typedef std::vector OptionArgVector; +typedef std::shared_ptr OptionArgVectorSP; + +struct OptionArgElement +{ + enum { + eUnrecognizedArg = -1, + eBareDash = -2, + eBareDoubleDash = -3 + }; + + OptionArgElement (int defs_index, int pos, int arg_pos) : + opt_defs_index(defs_index), + opt_pos (pos), + opt_arg_pos (arg_pos) + { + } + + int opt_defs_index; + int opt_pos; + int opt_arg_pos; +}; + +typedef std::vector OptionElementVector; + +//---------------------------------------------------------------------- +/// @class Args Args.h "lldb/Interpreter/Args.h" +/// @brief A command line argument class. +/// +/// The Args class is designed to be fed a command line. The +/// command line is copied into an internal buffer and then split up +/// into arguments. Arguments are space delimited if there are no quotes +/// (single, double, or backtick quotes) surrounding the argument. Spaces +/// can be escaped using a \ character to avoid having to surround an +/// argument that contains a space with quotes. +//---------------------------------------------------------------------- +class Args +{ +public: + + //------------------------------------------------------------------ + /// Construct with an option command string. + /// + /// @param[in] command + /// A NULL terminated command that will be copied and split up + /// into arguments. + /// + /// @see Args::SetCommandString(const char *) + //------------------------------------------------------------------ + Args (const char *command = NULL); + + Args (const char *command, size_t len); + + Args (const Args &rhs); + + const Args & + operator= (const Args &rhs); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Args(); + + //------------------------------------------------------------------ + /// Dump all arguments to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump all arguments in the argument + /// vector. + //------------------------------------------------------------------ + void + Dump (Stream *s); + + //------------------------------------------------------------------ + /// Sets the command string contained by this object. + /// + /// The command string will be copied and split up into arguments + /// that can be accessed via the accessor functions. + /// + /// @param[in] command + /// A NULL terminated command that will be copied and split up + /// into arguments. + /// + /// @see Args::GetArgumentCount() const + /// @see Args::GetArgumentAtIndex (size_t) const + /// @see Args::GetArgumentVector () + /// @see Args::Shift () + /// @see Args::Unshift (const char *) + //------------------------------------------------------------------ + void + SetCommandString (const char *command); + + void + SetCommandString (const char *command, size_t len); + + bool + GetCommandString (std::string &command) const; + + bool + GetQuotedCommandString (std::string &command) const; + + //------------------------------------------------------------------ + /// Gets the number of arguments left in this command object. + /// + /// @return + /// The number or arguments in this object. + //------------------------------------------------------------------ + size_t + GetArgumentCount () const; + + //------------------------------------------------------------------ + /// Gets the NULL terminated C string argument pointer for the + /// argument at index \a idx. + /// + /// @return + /// The NULL terminated C string argument pointer if \a idx is a + /// valid argument index, NULL otherwise. + //------------------------------------------------------------------ + const char * + GetArgumentAtIndex (size_t idx) const; + + char + GetArgumentQuoteCharAtIndex (size_t idx) const; + + //------------------------------------------------------------------ + /// Gets the argument vector. + /// + /// The value returned by this function can be used by any function + /// that takes and vector. The return value is just like \a argv + /// in the standard C entry point function: + /// \code + /// int main (int argc, const char **argv); + /// \endcode + /// + /// @return + /// An array of NULL terminated C string argument pointers that + /// also has a terminating NULL C string pointer + //------------------------------------------------------------------ + char ** + GetArgumentVector (); + + //------------------------------------------------------------------ + /// Gets the argument vector. + /// + /// The value returned by this function can be used by any function + /// that takes and vector. The return value is just like \a argv + /// in the standard C entry point function: + /// \code + /// int main (int argc, const char **argv); + /// \endcode + /// + /// @return + /// An array of NULL terminate C string argument pointers that + /// also has a terminating NULL C string pointer + //------------------------------------------------------------------ + const char ** + GetConstArgumentVector () const; + + + //------------------------------------------------------------------ + /// Appends a new argument to the end of the list argument list. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr. + //------------------------------------------------------------------ + const char * + AppendArgument (const char *arg_cstr, char quote_char = '\0'); + + void + AppendArguments (const Args &rhs); + + void + AppendArguments (const char **argv); + + //------------------------------------------------------------------ + /// Insert the argument value at index \a idx to \a arg_cstr. + /// + /// @param[in] idx + /// The index of where to insert the argument. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr. + //------------------------------------------------------------------ + const char * + InsertArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Replaces the argument value at index \a idx to \a arg_cstr + /// if \a idx is a valid argument index. + /// + /// @param[in] idx + /// The index of the argument that will have its value replaced. + /// + /// @param[in] arg_cstr + /// The new argument as a NULL terminated C string. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// The NULL terminated C string of the copy of \a arg_cstr if + /// \a idx was a valid index, NULL otherwise. + //------------------------------------------------------------------ + const char * + ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Deletes the argument value at index + /// if \a idx is a valid argument index. + /// + /// @param[in] idx + /// The index of the argument that will have its value replaced. + /// + //------------------------------------------------------------------ + void + DeleteArgumentAtIndex (size_t idx); + + //------------------------------------------------------------------ + /// Sets the argument vector value, optionally copying all + /// arguments into an internal buffer. + /// + /// Sets the arguments to match those found in \a argv. All argument + /// strings will be copied into an internal buffers. + // + // FIXME: Handle the quote character somehow. + //------------------------------------------------------------------ + void + SetArguments (size_t argc, const char **argv); + + void + SetArguments (const char **argv); + + //------------------------------------------------------------------ + /// Shifts the first argument C string value of the array off the + /// argument array. + /// + /// The string value will be freed, so a copy of the string should + /// be made by calling Args::GetArgumentAtIndex (size_t) const + /// first and copying the returned value before calling + /// Args::Shift(). + /// + /// @see Args::GetArgumentAtIndex (size_t) const + //------------------------------------------------------------------ + void + Shift (); + + //------------------------------------------------------------------ + /// Inserts a class owned copy of \a arg_cstr at the beginning of + /// the argument vector. + /// + /// A copy \a arg_cstr will be made. + /// + /// @param[in] arg_cstr + /// The argument to push on the front the the argument stack. + /// + /// @param[in] quote_char + /// If the argument was originally quoted, put in the quote char here. + /// + /// @return + /// A pointer to the copy of \a arg_cstr that was made. + //------------------------------------------------------------------ + const char * + Unshift (const char *arg_cstr, char quote_char = '\0'); + + //------------------------------------------------------------------ + /// Parse the arguments in the contained arguments. + /// + /// The arguments that are consumed by the argument parsing process + /// will be removed from the argument vector. The arguements that + /// get processed start at the second argument. The first argument + /// is assumed to be the command and will not be touched. + /// + /// @see class Options + //------------------------------------------------------------------ + Error + ParseOptions (Options &options); + + size_t + FindArgumentIndexForOption (struct option *long_options, int long_options_index); + + bool + IsPositionalArgument (const char *arg); + + // The following works almost identically to ParseOptions, except that no option is required to have arguments, + // and it builds up the option_arg_vector as it parses the options. + + void + ParseAliasOptions (Options &options, CommandReturnObject &result, OptionArgVector *option_arg_vector, + std::string &raw_input_line); + + void + ParseArgsForCompletion (Options &options, OptionElementVector &option_element_vector, uint32_t cursor_index); + + //------------------------------------------------------------------ + // Clear the arguments. + // + // For re-setting or blanking out the list of arguments. + //------------------------------------------------------------------ + void + Clear (); + + static const char * + StripSpaces (std::string &s, + bool leading = true, + bool trailing = true, + bool return_null_if_empty = true); + + static int32_t + StringToSInt32 (const char *s, int32_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static uint32_t + StringToUInt32 (const char *s, uint32_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static int64_t + StringToSInt64 (const char *s, int64_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static uint64_t + StringToUInt64 (const char *s, uint64_t fail_value = 0, int base = 0, bool *success_ptr = NULL); + + static bool + UInt64ValueIsValidForByteSize (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; + } + + static bool + SInt64ValueIsValidForByteSize (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; + } + + static lldb::addr_t + StringToAddress (const ExecutionContext *exe_ctx, + const char *s, + lldb::addr_t fail_value, + Error *error); + + static bool + StringToBoolean (const char *s, bool fail_value, bool *success_ptr); + + static int64_t + StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error); + + static lldb::ScriptLanguage + StringToScriptLanguage (const char *s, lldb::ScriptLanguage fail_value, bool *success_ptr); + + static Error + StringToFormat (const char *s, + lldb::Format &format, + size_t *byte_size_ptr); // If non-NULL, then a byte size can precede the format character + + static lldb::Encoding + StringToEncoding (const char *s, + lldb::Encoding fail_value = lldb::eEncodingInvalid); + + static uint32_t + StringToGenericRegister (const char *s); + + static const char * + StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update); + + static const char * + GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg); + + // EncodeEscapeSequences will change the textual representation of common + // escape sequences like "\n" (two characters) into a single '\n'. It does + // this for all of the supported escaped sequences and for the \0ooo (octal) + // and \xXX (hex). The resulting "dst" string will contain the character + // versions of all supported escape sequences. The common supported escape + // sequences are: "\a", "\b", "\f", "\n", "\r", "\t", "\v", "\'", "\"", "\\". + + static void + EncodeEscapeSequences (const char *src, std::string &dst); + + // ExpandEscapeSequences will change a string of possibly non-printable + // characters and expand them into text. So '\n' will turn into two chracters + // like "\n" which is suitable for human reading. When a character is not + // printable and isn't one of the common in escape sequences listed in the + // help for EncodeEscapeSequences, then it will be encoded as octal. Printable + // characters are left alone. + static void + ExpandEscapedCharacters (const char *src, std::string &dst); + + // This one isn't really relevant to Arguments per se, but we're using the Args as a + // general strings container, so... + void + LongestCommonPrefix (std::string &common_prefix); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Args can see and modify these + //------------------------------------------------------------------ + typedef std::list arg_sstr_collection; + typedef std::vector arg_cstr_collection; + typedef std::vector arg_quote_char_collection; + arg_sstr_collection m_args; + arg_cstr_collection m_argv; ///< The current argument vector. + arg_quote_char_collection m_args_quote_char; + + void + UpdateArgsAfterOptionParsing (); + + void + UpdateArgvFromArgs (); +}; + +} // namespace lldb_private + +#endif // liblldb_Command_h_ diff --git a/include/lldb/Interpreter/CommandCompletions.h b/include/lldb/Interpreter/CommandCompletions.h new file mode 100644 index 00000000000..c4ab1b61ade --- /dev/null +++ b/include/lldb/Interpreter/CommandCompletions.h @@ -0,0 +1,307 @@ +//===-- CommandCompletions.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_CommandCompletions_h_ +#define lldb_CommandCompletions_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/RegularExpression.h" + +namespace lldb_private +{ +class CommandCompletions +{ +public: + + //---------------------------------------------------------------------- + // This is the command completion callback that is used to complete the argument of the option + // it is bound to (in the OptionDefinition table below). Return the total number of matches. + //---------------------------------------------------------------------- + typedef int (*CompletionCallback) (CommandInterpreter &interpreter, + const char *completion_str, // This is the argument we are completing + int match_start_point, // This is the point in the list of matches that you should start returning elements + int max_return_elements, // This is the number of matches requested. + lldb_private::SearchFilter *searcher,// A search filter to limit the search... + bool &word_complete, + lldb_private::StringList &matches); // The array of matches we return. + typedef enum + { + eNoCompletion = 0u, + eSourceFileCompletion = (1u << 0), + eDiskFileCompletion = (1u << 1), + eDiskDirectoryCompletion = (1u << 2), + eSymbolCompletion = (1u << 3), + eModuleCompletion = (1u << 4), + eSettingsNameCompletion = (1u << 5), + ePlatformPluginCompletion = (1u << 6), + eArchitectureCompletion = (1u << 7), + eVariablePathCompletion = (1u << 8), + // This item serves two purposes. It is the last element in the enum, + // so you can add custom enums starting from here in your Option class. + // Also if you & in this bit the base code will not process the option. + eCustomCompletion = (1u << 9) + + } CommonCompletionTypes; + + struct CommonCompletionElement + { + uint32_t type; + CompletionCallback callback; + }; + + static bool InvokeCommonCompletionCallbacks (CommandInterpreter &interpreter, + uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + + //---------------------------------------------------------------------- + // These are the generic completer functions: + //---------------------------------------------------------------------- + static int + DiskFiles (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + static int + DiskDirectories (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + + static int + SourceFiles (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches); + + static int + Modules (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + Symbols (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + SettingsNames (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + PlatformPluginNames (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + + static int + ArchitectureNames (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + static int + VariablePath (CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches); + + //---------------------------------------------------------------------- + // The Completer class is a convenient base class for building searchers + // that go along with the SearchFilter passed to the standard Completer + // functions. + //---------------------------------------------------------------------- + class Completer : public Searcher + { + public: + Completer (CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual ~Completer (); + + virtual CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete) = 0; + + virtual Depth + GetDepth () = 0; + + virtual size_t + DoCompletion (SearchFilter *filter) = 0; + + protected: + CommandInterpreter &m_interpreter; + std::string m_completion_str; + int m_match_start_point; + int m_max_return_elements; + StringList &m_matches; + private: + DISALLOW_COPY_AND_ASSIGN (Completer); + }; + + //---------------------------------------------------------------------- + // SouceFileCompleter implements the source file completer + //---------------------------------------------------------------------- + class SourceFileCompleter : public Completer + { + public: + + SourceFileCompleter (CommandInterpreter &interpreter, + bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual Searcher::Depth GetDepth (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete); + + size_t + DoCompletion (SearchFilter *filter); + + private: + bool m_include_support_files; + FileSpecList m_matching_files; + const char *m_file_name; + const char *m_dir_name; + DISALLOW_COPY_AND_ASSIGN (SourceFileCompleter); + + }; + + //---------------------------------------------------------------------- + // ModuleCompleter implements the module completer + //---------------------------------------------------------------------- + class ModuleCompleter : public Completer + { + public: + + ModuleCompleter (CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual Searcher::Depth GetDepth (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete); + + size_t + DoCompletion (SearchFilter *filter); + + private: + const char *m_file_name; + const char *m_dir_name; + DISALLOW_COPY_AND_ASSIGN (ModuleCompleter); + + }; + + //---------------------------------------------------------------------- + // SymbolCompleter implements the symbol completer + //---------------------------------------------------------------------- + class SymbolCompleter : public Completer + { + public: + + SymbolCompleter (CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches); + + virtual Searcher::Depth GetDepth (); + + virtual Searcher::CallbackReturn + SearchCallback (SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete); + + size_t + DoCompletion (SearchFilter *filter); + + private: +// struct NameCmp { +// bool operator() (const ConstString& lhs, const ConstString& rhs) const +// { +// return lhs < rhs; +// } +// }; + + RegularExpression m_regex; + typedef std::set collection; + collection m_match_set; + DISALLOW_COPY_AND_ASSIGN (SymbolCompleter); + + }; + +private: + static CommonCompletionElement g_common_completions[]; + +}; + +} // namespace lldb_private +#endif // lldb_CommandCompletions_h_ diff --git a/include/lldb/Interpreter/CommandHistory.h b/include/lldb/Interpreter/CommandHistory.h new file mode 100644 index 00000000000..dbe6e99bb5b --- /dev/null +++ b/include/lldb/Interpreter/CommandHistory.h @@ -0,0 +1,76 @@ +//===-- CommandHistory.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandHistory_h_ +#define liblldb_CommandHistory_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class CommandHistory +{ +public: + CommandHistory (); + + ~CommandHistory (); + + size_t + GetSize () const; + + bool + IsEmpty () const; + + const char* + FindString (const char* input_str) const; + + const char* + GetStringAtIndex (size_t idx) const; + + const char* + operator [] (size_t idx) const; + + const char* + GetRecentmostString () const; + + void + AppendString (const std::string& str, + bool reject_if_dupe = true); + + void + Clear (); + + void + Dump (Stream& stream, + size_t start_idx = 0, + size_t stop_idx = SIZE_MAX) const; + + static const char g_repeat_char = '!'; + +private: + DISALLOW_COPY_AND_ASSIGN(CommandHistory); + + typedef std::vector History; + mutable Mutex m_mutex; + History m_history; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandHistory_h_ diff --git a/include/lldb/Interpreter/CommandInterpreter.h b/include/lldb/Interpreter/CommandInterpreter.h new file mode 100644 index 00000000000..31fcc38eed9 --- /dev/null +++ b/include/lldb/Interpreter/CommandInterpreter.h @@ -0,0 +1,486 @@ +//===-- CommandInterpreter.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandInterpreter_h_ +#define liblldb_CommandInterpreter_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/CommandHistory.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Core/Event.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/StringList.h" + +namespace lldb_private { + +class CommandInterpreter : + public Broadcaster, + public Properties +{ +public: + typedef std::map OptionArgMap; + + enum + { + eBroadcastBitThreadShouldExit = (1 << 0), + eBroadcastBitResetPrompt = (1 << 1), + eBroadcastBitQuitCommandReceived = (1 << 2), // User entered quit + eBroadcastBitAsynchronousOutputData = (1 << 3), + eBroadcastBitAsynchronousErrorData = (1 << 4) + }; + + enum ChildrenTruncatedWarningStatus // tristate boolean to manage children truncation warning + { + eNoTruncation = 0, // never truncated + eUnwarnedTruncation = 1, // truncated but did not notify + eWarnedTruncation = 2 // truncated and notified + }; + + enum CommandTypes + { + eCommandTypesBuiltin = 0x0001, // native commands such as "frame" + eCommandTypesUserDef = 0x0002, // scripted commands + eCommandTypesAliases = 0x0004, // aliases such as "po" + eCommandTypesAllThem = 0xFFFF // all commands + }; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + void + SourceInitFile (bool in_cwd, + CommandReturnObject &result); + + CommandInterpreter (Debugger &debugger, + lldb::ScriptLanguage script_language, + bool synchronous_execution); + + virtual + ~CommandInterpreter (); + + bool + AddCommand (const char *name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace); + + bool + AddUserCommand (std::string name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace); + + lldb::CommandObjectSP + GetCommandSPExact (const char *cmd, + bool include_aliases); + + CommandObject * + GetCommandObjectExact (const char *cmd_cstr, + bool include_aliases); + + CommandObject * + GetCommandObject (const char *cmd, + StringList *matches = NULL); + + bool + CommandExists (const char *cmd); + + bool + AliasExists (const char *cmd); + + bool + UserCommandExists (const char *cmd); + + void + AddAlias (const char *alias_name, + lldb::CommandObjectSP& command_obj_sp); + + bool + RemoveAlias (const char *alias_name); + + bool + GetAliasFullName (const char *cmd, std::string &full_name); + + bool + RemoveUser (const char *alias_name); + + void + RemoveAllUser () + { + m_user_dict.clear(); + } + + OptionArgVectorSP + GetAliasOptions (const char *alias_name); + + + bool + ProcessAliasOptionsArgs (lldb::CommandObjectSP &cmd_obj_sp, + const char *options_args, + OptionArgVectorSP &option_arg_vector_sp); + + void + RemoveAliasOptions (const char *alias_name); + + void + AddOrReplaceAliasOptions (const char *alias_name, + OptionArgVectorSP &option_arg_vector_sp); + + CommandObject * + BuildAliasResult (const char *alias_name, + std::string &raw_input_string, + std::string &alias_result, + CommandReturnObject &result); + + bool + HandleCommand (const char *command_line, + LazyBool add_to_history, + CommandReturnObject &result, + ExecutionContext *override_context = NULL, + bool repeat_on_empty_command = true, + bool no_context_switching = false); + + //------------------------------------------------------------------ + /// Execute a list of commands in sequence. + /// + /// @param[in] commands + /// The list of commands to execute. + /// @param[in/out] context + /// The execution context in which to run the commands. Can be NULL in which case the default + /// context will be used. + /// @param[in] stop_on_continue + /// If \b true execution will end on the first command that causes the process in the + /// execution context to continue. If \false, we won't check the execution status. + /// @param[in] stop_on_error + /// If \b true execution will end on the first command that causes an error. + /// @param[in] echo_commands + /// If \b true echo the command before executing it. If \false, execute silently. + /// @param[in] print_results + /// If \b true print the results of the command after executing it. If \false, execute silently. + /// @param[out] result + /// This is marked as succeeding with no output if all commands execute safely, + /// and failed with some explanation if we aborted executing the commands at some point. + //------------------------------------------------------------------ + void + HandleCommands (const StringList &commands, + ExecutionContext *context, + bool stop_on_continue, + bool stop_on_error, + bool echo_commands, + bool print_results, + LazyBool add_to_history, + CommandReturnObject &result); + + //------------------------------------------------------------------ + /// Execute a list of commands from a file. + /// + /// @param[in] file + /// The file from which to read in commands. + /// @param[in/out] context + /// The execution context in which to run the commands. Can be NULL in which case the default + /// context will be used. + /// @param[in] stop_on_continue + /// If \b true execution will end on the first command that causes the process in the + /// execution context to continue. If \false, we won't check the execution status. + /// @param[in] stop_on_error + /// If \b true execution will end on the first command that causes an error. + /// @param[in] echo_commands + /// If \b true echo the command before executing it. If \false, execute silently. + /// @param[in] print_results + /// If \b true print the results of the command after executing it. If \false, execute silently. + /// @param[out] result + /// This is marked as succeeding with no output if all commands execute safely, + /// and failed with some explanation if we aborted executing the commands at some point. + //------------------------------------------------------------------ + void + HandleCommandsFromFile (FileSpec &file, + ExecutionContext *context, + bool stop_on_continue, + bool stop_on_error, + bool echo_commands, + bool print_results, + LazyBool add_to_history, + CommandReturnObject &result); + + CommandObject * + GetCommandObjectForCommand (std::string &command_line); + + // This handles command line completion. You are given a pointer to the command string buffer, to the current cursor, + // and to the end of the string (in case it is not NULL terminated). + // You also passed in an StringList object to fill with the returns. + // The first element of the array will be filled with the string that you would need to insert at + // the cursor point to complete the cursor point to the longest common matching prefix. + // If you want to limit the number of elements returned, set max_return_elements to the number of elements + // you want returned. Otherwise set max_return_elements to -1. + // If you want to start some way into the match list, then set match_start_point to the desired start + // point. + // Returns: + // -1 if the completion character should be inserted + // -2 if the entire command line should be deleted and replaced with matches.GetStringAtIndex(0) + // INT_MAX if the number of matches is > max_return_elements, but it is expensive to compute. + // Otherwise, returns the number of matches. + // + // FIXME: Only max_return_elements == -1 is supported at present. + + int + HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + StringList &matches); + + // This version just returns matches, and doesn't compute the substring. It is here so the + // Help command can call it for the first argument. + // word_complete tells whether a the completions are considered a "complete" response (so the + // completer should complete the quote & put a space after the word. + + int + HandleCompletionMatches (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + + int + GetCommandNamesMatchingPartialString (const char *cmd_cstr, + bool include_aliases, + StringList &matches); + + void + GetHelp (CommandReturnObject &result, + uint32_t types = eCommandTypesAllThem); + + void + GetAliasHelp (const char *alias_name, + const char *command_name, + StreamString &help_string); + + void + OutputFormattedHelpText (Stream &stream, + const char *command_word, + const char *separator, + const char *help_text, + size_t max_word_len); + + // this mimics OutputFormattedHelpText but it does perform a much simpler + // formatting, basically ensuring line alignment. This is only good if you have + // some complicated layout for your help text and want as little help as reasonable + // in properly displaying it. Most of the times, you simply want to type some text + // and have it printed in a reasonable way on screen. If so, use OutputFormattedHelpText + void + OutputHelpText (Stream &stream, + const char *command_word, + const char *separator, + const char *help_text, + uint32_t max_word_len); + + Debugger & + GetDebugger () + { + return m_debugger; + } + + ExecutionContext + GetExecutionContext() + { + return m_exe_ctx_ref.Lock(); + } + + void + UpdateExecutionContext (ExecutionContext *override_context); + + lldb::PlatformSP + GetPlatform (bool prefer_target_platform); + + const char * + ProcessEmbeddedScriptCommands (const char *arg); + + const char * + GetPrompt (); + + void + SetPrompt (const char *); + + bool Confirm (const char *message, bool default_answer); + + static size_t + GetConfirmationInputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction action, + const char *bytes, + size_t bytes_len); + + void + LoadCommandDictionary (); + + void + Initialize (); + + void + SetScriptLanguage (lldb::ScriptLanguage lang); + + + bool + HasCommands (); + + bool + HasAliases (); + + bool + HasUserCommands (); + + bool + HasAliasOptions (); + + void + BuildAliasCommandArgs (CommandObject *alias_cmd_obj, + const char *alias_name, + Args &cmd_args, + std::string &raw_input_string, + CommandReturnObject &result); + + int + GetOptionArgumentPosition (const char *in_string); + + ScriptInterpreter * + GetScriptInterpreter (bool can_create = true); + + void + SkipLLDBInitFiles (bool skip_lldbinit_files) + { + m_skip_lldbinit_files = skip_lldbinit_files; + } + + void + SkipAppInitFiles (bool skip_app_init_files) + { + m_skip_app_init_files = m_skip_lldbinit_files; + } + + bool + GetSynchronous (); + + size_t + FindLongestCommandWord (CommandObject::CommandMap &dict); + + void + FindCommandsForApropos (const char *word, + StringList &commands_found, + StringList &commands_help, + bool search_builtin_commands, + bool search_user_commands); + + bool + GetBatchCommandMode () { return m_batch_command_mode; } + + void + SetBatchCommandMode (bool value) { m_batch_command_mode = value; } + + void + ChildrenTruncated () + { + if (m_truncation_warning == eNoTruncation) + m_truncation_warning = eUnwarnedTruncation; + } + + bool + TruncationWarningNecessary () + { + return (m_truncation_warning == eUnwarnedTruncation); + } + + void + TruncationWarningGiven () + { + m_truncation_warning = eWarnedTruncation; + } + + const char * + TruncationWarningText () + { + return "*** Some of your variables have more members than the debugger will show by default. To show all of them, you can either use the --show-all-children option to %s or raise the limit by changing the target.max-children-count setting.\n"; + } + + const CommandHistory& + GetCommandHistory () const + { + return m_command_history; + } + + CommandHistory& + GetCommandHistory () + { + return m_command_history; + } + + //------------------------------------------------------------------ + // Properties + //------------------------------------------------------------------ + bool + GetExpandRegexAliases () const; + + bool + GetPromptOnQuit () const; + + bool + GetStopCmdSourceOnError () const; + +protected: + friend class Debugger; + + void + SetSynchronous (bool value); + + lldb::CommandObjectSP + GetCommandSP (const char *cmd, bool include_aliases = true, bool exact = true, StringList *matches = NULL); + +private: + + Error + PreprocessCommand (std::string &command); + + Debugger &m_debugger; // The debugger session that this interpreter is associated with + ExecutionContextRef m_exe_ctx_ref; // The current execution context to use when handling commands + bool m_synchronous_execution; + bool m_skip_lldbinit_files; + bool m_skip_app_init_files; + CommandObject::CommandMap m_command_dict; // Stores basic built-in commands (they cannot be deleted, removed or overwritten). + CommandObject::CommandMap m_alias_dict; // Stores user aliases/abbreviations for commands + CommandObject::CommandMap m_user_dict; // Stores user-defined commands + OptionArgMap m_alias_options; // Stores any options (with or without arguments) that go with any alias. + CommandHistory m_command_history; + std::string m_repeat_command; // Stores the command that will be executed for an empty command string. + std::unique_ptr m_script_interpreter_ap; + char m_comment_char; + bool m_batch_command_mode; + ChildrenTruncatedWarningStatus m_truncation_warning; // Whether we truncated children and whether the user has been told + uint32_t m_command_source_depth; + +}; + + +} // namespace lldb_private + +#endif // liblldb_CommandInterpreter_h_ diff --git a/include/lldb/Interpreter/CommandObject.h b/include/lldb/Interpreter/CommandObject.h new file mode 100644 index 00000000000..2bfab0a8ecc --- /dev/null +++ b/include/lldb/Interpreter/CommandObject.h @@ -0,0 +1,608 @@ +//===-- CommandObject.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObject_h_ +#define liblldb_CommandObject_h_ + +#include +#include +#include +#include + +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Flags.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +class CommandObject +{ +public: + + typedef const char *(ArgumentHelpCallbackFunction) (); + + struct ArgumentHelpCallback + { + ArgumentHelpCallbackFunction *help_callback; + bool self_formatting; + + const char* + operator () () const + { + return (*help_callback)(); + } + + operator bool() const + { + return (help_callback != NULL); + } + + }; + + struct ArgumentTableEntry // Entries in the main argument information table + { + lldb::CommandArgumentType arg_type; + const char *arg_name; + CommandCompletions::CommonCompletionTypes completion_type; + ArgumentHelpCallback help_function; + const char *help_text; + }; + + struct CommandArgumentData // Used to build individual command argument lists + { + lldb::CommandArgumentType arg_type; + ArgumentRepetitionType arg_repetition; + uint32_t arg_opt_set_association; // This arg might be associated only with some particular option set(s). + CommandArgumentData(): + arg_type(lldb::eArgTypeNone), + arg_repetition(eArgRepeatPlain), + arg_opt_set_association(LLDB_OPT_SET_ALL) // By default, the arg associates to all option sets. + {} + }; + + typedef std::vector CommandArgumentEntry; // Used to build individual command argument lists + + static ArgumentTableEntry g_arguments_data[lldb::eArgTypeLastArg]; // Main argument information table + + typedef std::map CommandMap; + + CommandObject (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObject (); + + + static const char * + GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type); + + static const char * + GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type); + + CommandInterpreter & + GetCommandInterpreter () + { + return m_interpreter; + } + + const char * + GetHelp (); + + virtual const char * + GetHelpLong (); + + const char * + GetSyntax (); + + const char * + GetCommandName (); + + void + SetHelp (const char * str); + + void + SetHelpLong (const char * str); + + void + SetHelpLong (std::string str); + + void + SetSyntax (const char *str); + + // override this to return true if you want to enable the user to delete + // the Command object from the Command dictionary (aliases have their own + // deletion scheme, so they do not need to care about this) + virtual bool + IsRemovable () const { return false; } + + bool + IsAlias () { return m_is_alias; } + + void + SetIsAlias (bool value) { m_is_alias = value; } + + virtual bool + IsMultiwordObject () { return false; } + + virtual lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL) + { + return lldb::CommandObjectSP(); + } + + virtual CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL) + { + return NULL; + } + + virtual void + AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help) + { + } + + void + GenerateHelpText (CommandReturnObject &result); + + virtual void + GenerateHelpText (Stream &result); + + // this is needed in order to allow the SBCommand class to + // transparently try and load subcommands - it will fail on + // anything but a multiword command, but it avoids us doing + // type checkings and casts + virtual bool + LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_obj) + { + return false; + } + + virtual bool + WantsRawCommandString() = 0; + + // By default, WantsCompletion = !WantsRawCommandString. + // Subclasses who want raw command string but desire, for example, + // argument completion should override this method to return true. + virtual bool + WantsCompletion() { return !WantsRawCommandString(); } + + virtual Options * + GetOptions (); + + static const ArgumentTableEntry* + GetArgumentTable (); + + static lldb::CommandArgumentType + LookupArgumentName (const char *arg_name); + + static ArgumentTableEntry * + FindArgumentDataByType (lldb::CommandArgumentType arg_type); + + int + GetNumArgumentEntries (); + + CommandArgumentEntry * + GetArgumentEntryAtIndex (int idx); + + static void + GetArgumentHelp (Stream &str, lldb::CommandArgumentType arg_type, CommandInterpreter &interpreter); + + static const char * + GetArgumentName (lldb::CommandArgumentType arg_type); + + // Generates a nicely formatted command args string for help command output. + // By default, all possible args are taken into account, for example, + // ''. This can be refined by passing a second arg + // specifying which option set(s) we are interested, which could then, for + // example, produce either '' or ''. + void + GetFormattedCommandArguments (Stream &str, uint32_t opt_set_mask = LLDB_OPT_SET_ALL); + + bool + IsPairType (ArgumentRepetitionType arg_repeat_type); + + enum + { + //---------------------------------------------------------------------- + // eFlagRequiresTarget + // + // Ensures a valid target is contained in m_exe_ctx prior to executing + // the command. If a target doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidTargetDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidTargetDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresTarget = (1u << 0), + //---------------------------------------------------------------------- + // eFlagRequiresProcess + // + // Ensures a valid process is contained in m_exe_ctx prior to executing + // the command. If a process doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidProcessDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidProcessDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresProcess = (1u << 1), + //---------------------------------------------------------------------- + // eFlagRequiresThread + // + // Ensures a valid thread is contained in m_exe_ctx prior to executing + // the command. If a thread doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidThreadDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidThreadDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresThread = (1u << 2), + //---------------------------------------------------------------------- + // eFlagRequiresFrame + // + // Ensures a valid frame is contained in m_exe_ctx prior to executing + // the command. If a frame doesn't exist or is invalid, the command + // will fail and CommandObject::GetInvalidFrameDescription() will be + // returned as the error. CommandObject subclasses can override the + // virtual function for GetInvalidFrameDescription() to provide custom + // strings when needed. + //---------------------------------------------------------------------- + eFlagRequiresFrame = (1u << 3), + //---------------------------------------------------------------------- + // eFlagRequiresRegContext + // + // Ensures a valid register context (from the selected frame if there + // is a frame in m_exe_ctx, or from the selected thread from m_exe_ctx) + // is availble from m_exe_ctx prior to executing the command. If a + // target doesn't exist or is invalid, the command will fail and + // CommandObject::GetInvalidRegContextDescription() will be returned as + // the error. CommandObject subclasses can override the virtual function + // for GetInvalidRegContextDescription() to provide custom strings when + // needed. + //---------------------------------------------------------------------- + eFlagRequiresRegContext = (1u << 4), + //---------------------------------------------------------------------- + // eFlagTryTargetAPILock + // + // Attempts to acquire the target lock if a target is selected in the + // command interpreter. If the command object fails to acquire the API + // lock, the command will fail with an appropriate error message. + //---------------------------------------------------------------------- + eFlagTryTargetAPILock = (1u << 5), + //---------------------------------------------------------------------- + // eFlagProcessMustBeLaunched + // + // Verifies that there is a launched process in m_exe_ctx, if there + // isn't, the command will fail with an appropriate error message. + //---------------------------------------------------------------------- + eFlagProcessMustBeLaunched = (1u << 6), + //---------------------------------------------------------------------- + // eFlagProcessMustBePaused + // + // Verifies that there is a paused process in m_exe_ctx, if there + // isn't, the command will fail with an appropriate error message. + //---------------------------------------------------------------------- + eFlagProcessMustBePaused = (1u << 7) + }; + + bool + ParseOptions (Args& args, CommandReturnObject &result); + + void + SetCommandName (const char *name); + + // This function really deals with CommandObjectLists, but we didn't make a + // CommandObjectList class, so I'm sticking it here. But we really should have + // such a class. Anyway, it looks up the commands in the map that match the partial + // string cmd_str, inserts the matches into matches, and returns the number added. + + static int + AddNamesMatchingPartialString (CommandMap &in_map, const char *cmd_str, StringList &matches); + + //------------------------------------------------------------------ + /// The input array contains a parsed version of the line. The insertion + /// point is given by cursor_index (the index in input of the word containing + /// the cursor) and cursor_char_position (the position of the cursor in that word.) + /// This default version handles calling option argument completions and then calls + /// HandleArgumentCompletion if the cursor is on an argument, not an option. + /// Don't override this method, override HandleArgumentCompletion instead unless + /// you have special reasons. + /// + /// @param[in] interpreter + /// The command interpreter doing the completion. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] cursor_char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// FIXME: Not yet implemented... If there is a match that is expensive to compute, these are + /// here to allow you to compute the completions in batches. Start the completion from \amatch_start_point, + /// and return \amatch_return_elements elements. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will be inserted after the + /// completion.) \bfalse otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to make a distinction between + /// total number of matches, and the window the user wants returned. + /// + /// @return + /// \btrue if we were in an option, \bfalse otherwise. + //------------------------------------------------------------------ + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //------------------------------------------------------------------ + /// The input array contains a parsed version of the line. The insertion + /// point is given by cursor_index (the index in input of the word containing + /// the cursor) and cursor_char_position (the position of the cursor in that word.) + /// We've constructed the map of options and their arguments as well if that is + /// helpful for the completion. + /// + /// @param[in] interpreter + /// The command interpreter doing the completion. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] cursor_char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] opt_element_vector + /// The results of the options parse of \a input. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// See CommandObject::HandleCompletions for a description of how these work. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will be inserted after the + /// completion.) \bfalse otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to make a distinction between + /// total number of matches, and the window the user wants returned. + /// + /// @return + /// The number of completions. + //------------------------------------------------------------------ + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + return 0; + } + + bool + HelpTextContainsWord (const char *search_word); + + //------------------------------------------------------------------ + /// The flags accessor. + /// + /// @return + /// A reference to the Flags member variable. + //------------------------------------------------------------------ + Flags& + GetFlags() + { + return m_flags; + } + + //------------------------------------------------------------------ + /// The flags const accessor. + /// + /// @return + /// A const reference to the Flags member variable. + //------------------------------------------------------------------ + const Flags& + GetFlags() const + { + return m_flags; + } + + //------------------------------------------------------------------ + /// Get the command that appropriate for a "repeat" of the current command. + /// + /// @param[in] current_command_line + /// The complete current command line. + /// + /// @return + /// NULL if there is no special repeat command - it will use the current command line. + /// Otherwise a pointer to the command to be repeated. + /// If the returned string is the empty string, the command won't be repeated. + //------------------------------------------------------------------ + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return NULL; + } + + CommandOverrideCallback + GetOverrideCallback () const + { + return m_command_override_callback; + } + + void * + GetOverrideCallbackBaton () const + { + return m_command_override_baton; + } + + void + SetOverrideCallback (CommandOverrideCallback callback, void *baton) + { + m_command_override_callback = callback; + m_command_override_baton = baton; + } + + virtual bool + Execute (const char *args_string, CommandReturnObject &result) = 0; + +protected: + virtual const char * + GetInvalidTargetDescription() + { + return "invalid target, create a target using the 'target create' command"; + } + + virtual const char * + GetInvalidProcessDescription() + { + return "invalid process"; + } + + virtual const char * + GetInvalidThreadDescription() + { + return "invalid thread"; + } + + virtual const char * + GetInvalidFrameDescription() + { + return "invalid frame"; + } + + virtual const char * + GetInvalidRegContextDescription () + { + return "invalid frame, no registers"; + } + + //------------------------------------------------------------------ + /// Check the command to make sure anything required by this + /// command is available. + /// + /// @param[out] result + /// A command result object, if it is not okay to run the command + /// this will be filled in with a suitable error. + /// + /// @return + /// \b true if it is okay to run this command, \b false otherwise. + //------------------------------------------------------------------ + bool + CheckRequirements (CommandReturnObject &result); + + void + Cleanup (); + + CommandInterpreter &m_interpreter; + ExecutionContext m_exe_ctx; + Mutex::Locker m_api_locker; + std::string m_cmd_name; + std::string m_cmd_help_short; + std::string m_cmd_help_long; + std::string m_cmd_syntax; + bool m_is_alias; + Flags m_flags; + std::vector m_arguments; + CommandOverrideCallback m_command_override_callback; + void * m_command_override_baton; + + // Helper function to populate IDs or ID ranges as the command argument data + // to the specified command argument entry. + static void + AddIDsArgumentData(CommandArgumentEntry &arg, lldb::CommandArgumentType ID, lldb::CommandArgumentType IDRange); + +}; + +class CommandObjectParsed : public CommandObject +{ +public: + + CommandObjectParsed (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0) : + CommandObject (interpreter, name, help, syntax, flags) {} + + virtual + ~CommandObjectParsed () {}; + + virtual bool + Execute (const char *args_string, CommandReturnObject &result); + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) = 0; + + virtual bool + WantsRawCommandString() { return false; }; +}; + +class CommandObjectRaw : public CommandObject +{ +public: + + CommandObjectRaw (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0) : + CommandObject (interpreter, name, help, syntax, flags) {} + + virtual + ~CommandObjectRaw () {}; + + virtual bool + Execute (const char *args_string, CommandReturnObject &result); + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) = 0; + + virtual bool + WantsRawCommandString() { return true; }; +}; + + +} // namespace lldb_private + + +#endif // liblldb_CommandObject_h_ diff --git a/include/lldb/Interpreter/CommandObjectMultiword.h b/include/lldb/Interpreter/CommandObjectMultiword.h new file mode 100644 index 00000000000..491d43c4bd9 --- /dev/null +++ b/include/lldb/Interpreter/CommandObjectMultiword.h @@ -0,0 +1,187 @@ +//===-- CommandObjectMultiword.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectMultiword_h_ +#define liblldb_CommandObjectMultiword_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiword +//------------------------------------------------------------------------- + +class CommandObjectMultiword : public CommandObject +{ +// These two want to iterate over the subcommand dictionary. +friend class CommandInterpreter; +friend class CommandObjectSyntax; +public: + CommandObjectMultiword (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObjectMultiword (); + + virtual bool + IsMultiwordObject () { return true; } + + virtual bool + LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_obj); + + virtual void + GenerateHelpText (Stream &output_stream); + + virtual lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL); + + virtual CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL); + + virtual void + AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help); + + virtual bool + WantsRawCommandString() { return false; }; + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index); + + virtual bool + Execute (const char *args_string, + CommandReturnObject &result); + + virtual bool + IsRemovable() const { return m_can_be_removed; } + + void + SetRemovable (bool removable) + { + m_can_be_removed = removable; + } + +protected: + + CommandObject::CommandMap m_subcommand_dict; + bool m_can_be_removed; +}; + + +class CommandObjectProxy : public CommandObject +{ +public: + CommandObjectProxy (CommandInterpreter &interpreter, + const char *name, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0); + + virtual + ~CommandObjectProxy (); + + // Subclasses must provide a command object that will be transparently + // used for this object. + virtual CommandObject * + GetProxyCommandObject() = 0; + + virtual const char * + GetHelpLong (); + + virtual bool + IsRemovable() const; + + virtual bool + IsMultiwordObject (); + + virtual lldb::CommandObjectSP + GetSubcommandSP (const char *sub_cmd, StringList *matches = NULL); + + virtual CommandObject * + GetSubcommandObject (const char *sub_cmd, StringList *matches = NULL); + + virtual void + AproposAllSubCommands (const char *prefix, + const char *search_word, + StringList &commands_found, + StringList &commands_help); + + virtual bool + LoadSubCommand (const char *cmd_name, + const lldb::CommandObjectSP& command_obj); + + virtual bool + WantsRawCommandString(); + + virtual bool + WantsCompletion(); + + virtual Options * + GetOptions (); + + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + virtual const char * + GetRepeatCommand (Args ¤t_command_args, + uint32_t index); + + virtual bool + Execute (const char *args_string, + CommandReturnObject &result); + +protected: + + // These two want to iterate over the subcommand dictionary. + friend class CommandInterpreter; + friend class CommandObjectSyntax; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectMultiword_h_ diff --git a/include/lldb/Interpreter/CommandObjectRegexCommand.h b/include/lldb/Interpreter/CommandObjectRegexCommand.h new file mode 100644 index 00000000000..8855680d5f8 --- /dev/null +++ b/include/lldb/Interpreter/CommandObjectRegexCommand.h @@ -0,0 +1,81 @@ +//===-- CommandObjectRegexCommand.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectRegexCommand_h_ +#define liblldb_CommandObjectRegexCommand_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/RegularExpression.h" +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectRegexCommand +//------------------------------------------------------------------------- + +class CommandObjectRegexCommand : public CommandObjectRaw +{ +public: + + CommandObjectRegexCommand (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t max_matches, + uint32_t completion_type_mask = 0); + + virtual + ~CommandObjectRegexCommand (); + + bool + AddRegexCommand (const char *re_cstr, const char *command_cstr); + + bool + HasRegexEntries () const + { + return !m_entries.empty(); + } + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result); + + struct Entry + { + RegularExpression regex; + std::string command; + }; + + typedef std::list EntryCollection; + const uint32_t m_max_matches; + const uint32_t m_completion_type_mask; + EntryCollection m_entries; + +private: + DISALLOW_COPY_AND_ASSIGN (CommandObjectRegexCommand); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectRegexCommand_h_ diff --git a/include/lldb/Interpreter/CommandReturnObject.h b/include/lldb/Interpreter/CommandReturnObject.h new file mode 100644 index 00000000000..acd03992e5e --- /dev/null +++ b/include/lldb/Interpreter/CommandReturnObject.h @@ -0,0 +1,183 @@ +//===-- CommandReturnObject.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandReturnObject_h_ +#define liblldb_CommandReturnObject_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/StreamTee.h" + +namespace lldb_private { + + +class CommandReturnObject +{ +public: + + CommandReturnObject (); + + ~CommandReturnObject (); + + const char * + GetOutputData () + { + lldb::StreamSP stream_sp (m_out_stream.GetStreamAtIndex (eStreamStringIndex)); + if (stream_sp) + return static_cast(stream_sp.get())->GetData(); + return ""; + } + + const char * + GetErrorData () + { + lldb::StreamSP stream_sp (m_err_stream.GetStreamAtIndex (eStreamStringIndex)); + if (stream_sp) + return static_cast(stream_sp.get())->GetData(); + else + return ""; + } + + Stream & + GetOutputStream () + { + // Make sure we at least have our normal string stream output stream + lldb::StreamSP stream_sp (m_out_stream.GetStreamAtIndex (eStreamStringIndex)); + if (!stream_sp) + { + stream_sp.reset (new StreamString()); + m_out_stream.SetStreamAtIndex (eStreamStringIndex, stream_sp); + } + return m_out_stream; + } + + Stream & + GetErrorStream () + { + // Make sure we at least have our normal string stream output stream + lldb::StreamSP stream_sp (m_err_stream.GetStreamAtIndex (eStreamStringIndex)); + if (!stream_sp) + { + stream_sp.reset (new StreamString()); + m_err_stream.SetStreamAtIndex (eStreamStringIndex, stream_sp); + } + return m_err_stream; + } + + void + SetImmediateOutputFile (FILE *fh, bool transfer_fh_ownership = false) + { + lldb::StreamSP stream_sp (new StreamFile (fh, transfer_fh_ownership)); + m_out_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + void + SetImmediateErrorFile (FILE *fh, bool transfer_fh_ownership = false) + { + lldb::StreamSP stream_sp (new StreamFile (fh, transfer_fh_ownership)); + m_err_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + void + SetImmediateOutputStream (const lldb::StreamSP &stream_sp) + { + m_out_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + void + SetImmediateErrorStream (const lldb::StreamSP &stream_sp) + { + m_err_stream.SetStreamAtIndex (eImmediateStreamIndex, stream_sp); + } + + lldb::StreamSP + GetImmediateOutputStream () + { + return m_out_stream.GetStreamAtIndex (eImmediateStreamIndex); + } + + lldb::StreamSP + GetImmediateErrorStream () + { + return m_err_stream.GetStreamAtIndex (eImmediateStreamIndex); + } + + void + Clear(); + + void + AppendMessage (const char *in_string); + + void + AppendMessageWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + AppendRawWarning (const char *in_string); + + void + AppendWarning (const char *in_string); + + void + AppendWarningWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + AppendError (const char *in_string); + + void + AppendRawError (const char *in_string); + + void + AppendErrorWithFormat (const char *format, ...) __attribute__ ((format (printf, 2, 3))); + + void + SetError (const Error &error, + const char *fallback_error_cstr = NULL); + + void + SetError (const char *error_cstr); + + lldb::ReturnStatus + GetStatus(); + + void + SetStatus (lldb::ReturnStatus status); + + bool + Succeeded (); + + bool + HasResult (); + + bool GetDidChangeProcessState (); + + void SetDidChangeProcessState (bool b); + +private: + enum + { + eStreamStringIndex = 0, + eImmediateStreamIndex = 1 + }; + + StreamTee m_out_stream; + StreamTee m_err_stream; + + lldb::ReturnStatus m_status; + bool m_did_change_process_state; +}; + +} // namespace lldb_private + +#endif // liblldb_CommandReturnObject_h_ diff --git a/include/lldb/Interpreter/OptionGroupArchitecture.h b/include/lldb/Interpreter/OptionGroupArchitecture.h new file mode 100644 index 00000000000..7cd1ca3d710 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupArchitecture.h @@ -0,0 +1,73 @@ +//===-- OptionGroupArchitecture.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupArchitecture_h_ +#define liblldb_OptionGroupArchitecture_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/ArchSpec.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupArchitecture +//------------------------------------------------------------------------- + +class OptionGroupArchitecture : public OptionGroup +{ +public: + + OptionGroupArchitecture (); + + virtual + ~OptionGroupArchitecture (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + bool + GetArchitecture (Platform *platform, ArchSpec &arch); + + bool + ArchitectureWasSpecified () const + { + return !m_arch_str.empty(); + } + const char * + GetArchitectureName () + { + if (m_arch_str.empty()) + return NULL; + return m_arch_str.c_str(); + } + +protected: + + std::string m_arch_str; // Save the arch triple in case a platform is specified after the architecture +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupArchitecture_h_ diff --git a/include/lldb/Interpreter/OptionGroupBoolean.h b/include/lldb/Interpreter/OptionGroupBoolean.h new file mode 100644 index 00000000000..0d861b24169 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupBoolean.h @@ -0,0 +1,83 @@ +//===-- OptionGroupBoolean.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupBoolean_h_ +#define liblldb_OptionGroupBoolean_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueBoolean.h" + +namespace lldb_private { + //------------------------------------------------------------------------- + // OptionGroupBoolean + //------------------------------------------------------------------------- + + class OptionGroupBoolean : public OptionGroup + { + public: + // When 'no_argument_toggle_default' is true, then setting the option + // value does NOT require an argument, it sets the boolean value to the + // inverse of the default value + 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); + + virtual + ~OptionGroupBoolean (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueBoolean & + GetOptionValue () + { + return m_value; + } + + const OptionValueBoolean & + GetOptionValue () const + { + return m_value; + } + + protected: + OptionValueBoolean m_value; + OptionDefinition m_option_definition; + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupBoolean_h_ diff --git a/include/lldb/Interpreter/OptionGroupFile.h b/include/lldb/Interpreter/OptionGroupFile.h new file mode 100644 index 00000000000..632a2dbdf22 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupFile.h @@ -0,0 +1,142 @@ +//===-- OptionGroupFile.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupFile_h_ +#define liblldb_OptionGroupFile_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupFile +//------------------------------------------------------------------------- + +class OptionGroupFile : public OptionGroup +{ +public: + + 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); + + virtual + ~OptionGroupFile (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueFileSpec & + GetOptionValue () + { + return m_file; + } + + const OptionValueFileSpec & + GetOptionValue () const + { + return m_file; + } + +protected: + OptionValueFileSpec m_file; + OptionDefinition m_option_definition; + +}; + +//------------------------------------------------------------------------- +// OptionGroupFileList +//------------------------------------------------------------------------- + +class OptionGroupFileList : public OptionGroup +{ +public: + + 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); + + virtual + ~OptionGroupFileList (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + + OptionValueFileSpecList & + GetOptionValue () + { + return m_file_list; + } + + const OptionValueFileSpecList & + GetOptionValue () const + { + return m_file_list; + } + +protected: + OptionValueFileSpecList m_file_list; + OptionDefinition m_option_definition; + +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupFile_h_ diff --git a/include/lldb/Interpreter/OptionGroupFormat.h b/include/lldb/Interpreter/OptionGroupFormat.h new file mode 100644 index 00000000000..7419b049666 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupFormat.h @@ -0,0 +1,133 @@ +//===-- OptionGroupFormat.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupFormat_h_ +#define liblldb_OptionGroupFormat_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueFormat.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueUInt64.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupFormat +//------------------------------------------------------------------------- + +class OptionGroupFormat : public OptionGroup +{ +public: + static const uint32_t OPTION_GROUP_FORMAT = LLDB_OPT_SET_1; + static const uint32_t OPTION_GROUP_GDB_FMT = LLDB_OPT_SET_2; + static const uint32_t OPTION_GROUP_SIZE = LLDB_OPT_SET_3; + static const uint32_t OPTION_GROUP_COUNT = LLDB_OPT_SET_4; + + OptionGroupFormat (lldb::Format default_format, + uint64_t default_byte_size = UINT64_MAX, // Pass UINT64_MAX to disable the "--size" option + uint64_t default_count = UINT64_MAX); // Pass UINT64_MAX to disable the "--count" option + + virtual + ~OptionGroupFormat (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + lldb::Format + GetFormat () const + { + return m_format.GetCurrentValue(); + } + + OptionValueFormat & + GetFormatValue() + { + return m_format; + } + + const OptionValueFormat & + GetFormatValue() const + { + return m_format; + } + + OptionValueUInt64 & + GetByteSizeValue() + { + return m_byte_size; + } + + const OptionValueUInt64 & + GetByteSizeValue() const + { + return m_byte_size; + } + + OptionValueUInt64 & + GetCountValue() + { + return m_count; + } + + const OptionValueUInt64 & + GetCountValue() const + { + return m_count; + } + + bool + HasGDBFormat () const + { + return m_has_gdb_format; + } + + bool + AnyOptionWasSet () const + { + return m_format.OptionWasSet() || + m_byte_size.OptionWasSet() || + m_count.OptionWasSet(); + } + +protected: + + bool + ParserGDBFormatLetter (CommandInterpreter &interpreter, + char format_letter, + lldb::Format &format, + uint32_t &byte_size); + + OptionValueFormat m_format; + OptionValueUInt64 m_byte_size; + OptionValueUInt64 m_count; + char m_prev_gdb_format; + char m_prev_gdb_size; + + bool m_has_gdb_format; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupFormat_h_ diff --git a/include/lldb/Interpreter/OptionGroupOutputFile.h b/include/lldb/Interpreter/OptionGroupOutputFile.h new file mode 100644 index 00000000000..533cd6ee8eb --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupOutputFile.h @@ -0,0 +1,76 @@ +//===-- OptionGroupOutputFile.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupOutputFile_h_ +#define liblldb_OptionGroupOutputFile_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" + +namespace lldb_private { +//------------------------------------------------------------------------- +// OptionGroupOutputFile +//------------------------------------------------------------------------- + +class OptionGroupOutputFile : public OptionGroup +{ +public: + + OptionGroupOutputFile (); + + virtual + ~OptionGroupOutputFile (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + const OptionValueFileSpec & + GetFile () + { + return m_file; + } + + const OptionValueBoolean & + GetAppend () + { + return m_append; + } + + bool + AnyOptionWasSet () const + { + return m_file.OptionWasSet() || m_append.OptionWasSet(); + } + +protected: + OptionValueFileSpec m_file; + OptionValueBoolean m_append; + +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupOutputFile_h_ diff --git a/include/lldb/Interpreter/OptionGroupPlatform.h b/include/lldb/Interpreter/OptionGroupPlatform.h new file mode 100644 index 00000000000..970ad328ccb --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupPlatform.h @@ -0,0 +1,120 @@ +//===-- OptionGroupPlatform.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupPlatform_h_ +#define liblldb_OptionGroupPlatform_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// PlatformOptionGroup +// +// Make platform options available to any commands that need the settings. +//------------------------------------------------------------------------- +class OptionGroupPlatform : public OptionGroup +{ +public: + + OptionGroupPlatform (bool include_platform_option) : + OptionGroup(), + m_platform_name (), + m_sdk_sysroot (), + m_os_version_major (UINT32_MAX), + m_os_version_minor (UINT32_MAX), + m_os_version_update (UINT32_MAX), + m_include_platform_option (include_platform_option) + { + } + + virtual + ~OptionGroupPlatform () + { + } + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + lldb::PlatformSP + CreatePlatformWithOptions (CommandInterpreter &interpreter, + const ArchSpec &arch, + bool make_selected, + Error& error, + ArchSpec &platform_arch) const; + + bool + PlatformWasSpecified () const + { + return !m_platform_name.empty(); + } + + void + SetPlatformName (const char *platform_name) + { + if (platform_name && platform_name[0]) + m_platform_name.assign (platform_name); + else + m_platform_name.clear(); + } + + const ConstString & + GetSDKRootDirectory () const + { + return m_sdk_sysroot; + } + + void + SetSDKRootDirectory (const ConstString &sdk_root_directory) + { + m_sdk_sysroot = sdk_root_directory; + } + + const ConstString & + GetSDKBuild () const + { + return m_sdk_build; + } + + void + SetSDKBuild (const ConstString &sdk_build) + { + m_sdk_build = sdk_build; + } + + +protected: + std::string m_platform_name; + ConstString m_sdk_sysroot; + ConstString m_sdk_build; + uint32_t m_os_version_major; + uint32_t m_os_version_minor; + uint32_t m_os_version_update; + bool m_include_platform_option; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupPlatform_h_ diff --git a/include/lldb/Interpreter/OptionGroupString.h b/include/lldb/Interpreter/OptionGroupString.h new file mode 100644 index 00000000000..e62a81bc411 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupString.h @@ -0,0 +1,82 @@ +//===-- OptionGroupString.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupString_h_ +#define liblldb_OptionGroupString_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueString.h" + +namespace lldb_private { + //------------------------------------------------------------------------- + // OptionGroupString + //------------------------------------------------------------------------- + + class OptionGroupString : public OptionGroup + { + public: + + 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); + + virtual + ~OptionGroupString (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueString & + GetOptionValue () + { + return m_value; + } + + const OptionValueString & + GetOptionValue () const + { + return m_value; + } + + protected: + OptionValueString m_value; + OptionDefinition m_option_definition; + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupString_h_ diff --git a/include/lldb/Interpreter/OptionGroupUInt64.h b/include/lldb/Interpreter/OptionGroupUInt64.h new file mode 100644 index 00000000000..c5f9e85d2f8 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupUInt64.h @@ -0,0 +1,82 @@ +//===-- OptionGroupUInt64.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupUInt64_h_ +#define liblldb_OptionGroupUInt64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueUInt64.h" + +namespace lldb_private { + //------------------------------------------------------------------------- + // OptionGroupUInt64 + //------------------------------------------------------------------------- + + class OptionGroupUInt64 : public OptionGroup + { + public: + + 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); + + virtual + ~OptionGroupUInt64 (); + + + virtual uint32_t + GetNumDefinitions () + { + return 1; + } + + virtual const OptionDefinition* + GetDefinitions () + { + return &m_option_definition; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + OptionValueUInt64 & + GetOptionValue () + { + return m_value; + } + + const OptionValueUInt64 & + GetOptionValue () const + { + return m_value; + } + + protected: + OptionValueUInt64 m_value; + OptionDefinition m_option_definition; + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupUInt64_h_ diff --git a/include/lldb/Interpreter/OptionGroupUUID.h b/include/lldb/Interpreter/OptionGroupUUID.h new file mode 100644 index 00000000000..ea968d73796 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupUUID.h @@ -0,0 +1,61 @@ +//===-- OptionGroupUUID.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupUUID_h_ +#define liblldb_OptionGroupUUID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionValueUUID.h" + +namespace lldb_private { +//------------------------------------------------------------------------- +// OptionGroupUUID +//------------------------------------------------------------------------- + +class OptionGroupUUID : public OptionGroup +{ +public: + + OptionGroupUUID (); + + virtual + ~OptionGroupUUID (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + const OptionValueUUID & + GetOptionValue () const + { + return m_uuid; + } + +protected: + OptionValueUUID m_uuid; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupUUID_h_ diff --git a/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h b/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h new file mode 100644 index 00000000000..da05e127d5d --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h @@ -0,0 +1,85 @@ +//===-- OptionGroupValueObjectDisplay.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupValueObjectDisplay_h_ +#define liblldb_OptionGroupValueObjectDisplay_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupValueObjectDisplay +//------------------------------------------------------------------------- + +class OptionGroupValueObjectDisplay : public OptionGroup +{ +public: + + OptionGroupValueObjectDisplay (); + + virtual + ~OptionGroupValueObjectDisplay (); + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + bool + AnyOptionWasSet () const + { + return show_types == true || + no_summary_depth != 0 || + show_location == true || + flat_output == true || + use_objc == true || + max_depth != UINT32_MAX || + ptr_depth != 0 || + use_synth == false || + be_raw == true || + ignore_cap == true; + } + + ValueObject::DumpValueObjectOptions + GetAsDumpOptions (bool objc_is_compact = false, + lldb::Format format = lldb::eFormatDefault, + lldb::TypeSummaryImplSP summary_sp = lldb::TypeSummaryImplSP()); + + bool show_types; + uint32_t no_summary_depth; + bool show_location; + bool flat_output; + bool use_objc; + uint32_t max_depth; + uint32_t ptr_depth; + lldb::DynamicValueType use_dynamic; + bool use_synth; + bool be_raw; + bool ignore_cap; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupValueObjectDisplay_h_ diff --git a/include/lldb/Interpreter/OptionGroupVariable.h b/include/lldb/Interpreter/OptionGroupVariable.h new file mode 100644 index 00000000000..40f4d436bc6 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupVariable.h @@ -0,0 +1,65 @@ +//===-- OptionGroupVariable.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupVariable_h_ +#define liblldb_OptionGroupVariable_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupVariable +//------------------------------------------------------------------------- + + class OptionGroupVariable : public OptionGroup + { + public: + + OptionGroupVariable (bool show_frame_options); + + virtual + ~OptionGroupVariable (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + bool include_frame_options:1, + show_args:1, // Frame option only (include_frame_options == true) + show_locals:1, // Frame option only (include_frame_options == true) + show_globals:1, // Frame option only (include_frame_options == true) + use_regex:1, + show_scope:1, + show_decl:1; + OptionValueString summary; // the name of a named summary + OptionValueString summary_string; // a summary string + + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupVariable); + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupVariable_h_ diff --git a/include/lldb/Interpreter/OptionGroupWatchpoint.h b/include/lldb/Interpreter/OptionGroupWatchpoint.h new file mode 100644 index 00000000000..1298da80750 --- /dev/null +++ b/include/lldb/Interpreter/OptionGroupWatchpoint.h @@ -0,0 +1,71 @@ +//===-- OptionGroupWatchpoint.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionGroupWatchpoint_h_ +#define liblldb_OptionGroupWatchpoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// OptionGroupWatchpoint +//------------------------------------------------------------------------- + + class OptionGroupWatchpoint : public OptionGroup + { + public: + + static bool + IsWatchSizeSupported(uint32_t watch_size); + + OptionGroupWatchpoint (); + + virtual + ~OptionGroupWatchpoint (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + // Note: + // eWatchRead == LLDB_WATCH_TYPE_READ; and + // eWatchWrite == LLDB_WATCH_TYPE_WRITE + typedef enum WatchType { + eWatchInvalid = 0, + eWatchRead, + eWatchWrite, + eWatchReadWrite + } WatchType; + + WatchType watch_type; + uint32_t watch_size; + bool watch_type_specified; + + private: + DISALLOW_COPY_AND_ASSIGN(OptionGroupWatchpoint); + }; + +} // namespace lldb_private + +#endif // liblldb_OptionGroupWatchpoint_h_ diff --git a/include/lldb/Interpreter/OptionValue.h b/include/lldb/Interpreter/OptionValue.h new file mode 100644 index 00000000000..33e7fc5f818 --- /dev/null +++ b/include/lldb/Interpreter/OptionValue.h @@ -0,0 +1,384 @@ +//===-- OptionValue.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValue_h_ +#define liblldb_OptionValue_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" + +namespace lldb_private { + + //--------------------------------------------------------------------- + // OptionValue + //--------------------------------------------------------------------- + class OptionValue + { + public: + typedef enum { + eTypeInvalid = 0, + eTypeArch, + eTypeArgs, + eTypeArray, + eTypeBoolean, + eTypeDictionary, + eTypeEnum, + eTypeFileSpec, + eTypeFileSpecList, + eTypeFormat, + eTypePathMap, + eTypeProperties, + eTypeRegex, + eTypeSInt64, + eTypeString, + eTypeUInt64, + eTypeUUID + } Type; + + enum { + eDumpOptionName = (1u << 0), + eDumpOptionType = (1u << 1), + eDumpOptionValue = (1u << 2), + eDumpOptionDescription = (1u << 3), + eDumpOptionRaw = (1u << 4), + eDumpGroupValue = (eDumpOptionName | eDumpOptionType | eDumpOptionValue), + eDumpGroupHelp = (eDumpOptionName | eDumpOptionType | eDumpOptionDescription) + }; + + + OptionValue () : + m_value_was_set (false) + { + } + + OptionValue (const OptionValue &rhs) : + m_value_was_set (rhs.m_value_was_set) + { + } + + virtual ~OptionValue () + { + } + //----------------------------------------------------------------- + // Subclasses should override these functions + //----------------------------------------------------------------- + virtual Type + GetType () const = 0; + + // If this value is always hidden, the avoid showing any info on this + // value, just show the info for the child values. + virtual bool + ValueIsTransparent () const + { + return GetType() == eTypeProperties; + } + + virtual const char * + GetTypeAsCString () const + { + return GetBuiltinTypeAsCString(GetType()); + } + + + static const char * + GetBuiltinTypeAsCString (Type t); + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask) = 0; + + virtual Error + SetValueFromCString (const char *value, VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () = 0; + + virtual lldb::OptionValueSP + DeepCopy () const = 0; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //----------------------------------------------------------------- + // Subclasses can override these functions + //----------------------------------------------------------------- + virtual lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const + { + error.SetErrorStringWithFormat("'%s' is not a value subvalue", name); + return lldb::OptionValueSP(); + } + + virtual Error + SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value); + + virtual bool + IsAggregateValue () const + { + return false; + } + + virtual ConstString + GetName() const + { + return ConstString(); + } + + virtual bool + DumpQualifiedName (Stream &strm) const; + //----------------------------------------------------------------- + // Subclasses should NOT override these functions as they use the + // above functions to implement functionality + //----------------------------------------------------------------- + uint32_t + GetTypeAsMask () + { + return 1u << GetType(); + } + + static uint32_t + ConvertTypeToMask (OptionValue::Type type) + { + return 1u << type; + } + + static OptionValue::Type + ConvertTypeMaskToType (uint32_t type_mask) + { + // If only one bit is set, then return an appropriate enumeration + switch (type_mask) + { + case 1u << eTypeArch: return eTypeArch; + case 1u << eTypeArgs: return eTypeArgs; + case 1u << eTypeArray: return eTypeArray; + case 1u << eTypeBoolean: return eTypeBoolean; + case 1u << eTypeDictionary: return eTypeDictionary; + case 1u << eTypeEnum: return eTypeEnum; + case 1u << eTypeFileSpec: return eTypeFileSpec; + case 1u << eTypeFileSpecList: return eTypeFileSpecList; + case 1u << eTypeFormat: return eTypeFormat; + case 1u << eTypePathMap: return eTypePathMap; + case 1u << eTypeProperties: return eTypeProperties; + case 1u << eTypeRegex: return eTypeRegex; + case 1u << eTypeSInt64: return eTypeSInt64; + case 1u << eTypeString: return eTypeString; + case 1u << eTypeUInt64: return eTypeUInt64; + case 1u << eTypeUUID: return eTypeUUID; + } + // Else return invalid + return eTypeInvalid; + } + + static lldb::OptionValueSP + CreateValueFromCStringForTypeMask (const char *value_cstr, + uint32_t type_mask, + Error &error); + + // 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 + GetUInt64Value (uint64_t fail_value, bool *success_ptr); + + OptionValueArch * + GetAsArch (); + + const OptionValueArch * + GetAsArch () const; + + OptionValueArray * + GetAsArray (); + + const OptionValueArray * + GetAsArray () const; + + OptionValueArgs * + GetAsArgs (); + + const OptionValueArgs * + GetAsArgs () const; + + OptionValueBoolean * + GetAsBoolean (); + + const OptionValueBoolean * + GetAsBoolean () const; + + OptionValueDictionary * + GetAsDictionary (); + + const OptionValueDictionary * + GetAsDictionary () const; + + OptionValueEnumeration * + GetAsEnumeration (); + + const OptionValueEnumeration * + GetAsEnumeration () const; + + OptionValueFileSpec * + GetAsFileSpec (); + + const OptionValueFileSpec * + GetAsFileSpec () const; + + OptionValueFileSpecList * + GetAsFileSpecList (); + + const OptionValueFileSpecList * + GetAsFileSpecList () const; + + OptionValueFormat * + GetAsFormat (); + + const OptionValueFormat * + GetAsFormat () const; + + OptionValuePathMappings * + GetAsPathMappings (); + + const OptionValuePathMappings * + GetAsPathMappings () const; + + OptionValueProperties * + GetAsProperties (); + + const OptionValueProperties * + GetAsProperties () const; + + OptionValueRegex * + GetAsRegex (); + + const OptionValueRegex * + GetAsRegex () const; + + OptionValueSInt64 * + GetAsSInt64 (); + + const OptionValueSInt64 * + GetAsSInt64 () const; + + OptionValueString * + GetAsString (); + + const OptionValueString * + GetAsString () const; + + OptionValueUInt64 * + GetAsUInt64 (); + + const OptionValueUInt64 * + GetAsUInt64 () const; + + OptionValueUUID * + GetAsUUID (); + + const OptionValueUUID * + GetAsUUID () const; + + bool + GetBooleanValue (bool fail_value = false) const; + + bool + SetBooleanValue (bool new_value); + + int64_t + GetEnumerationValue (int64_t fail_value = -1) const; + + bool + SetEnumerationValue (int64_t value); + + FileSpec + GetFileSpecValue () const; + + bool + SetFileSpecValue (const FileSpec &file_spec); + + FileSpecList + GetFileSpecListValue () const; + + lldb::Format + GetFormatValue (lldb::Format fail_value = lldb::eFormatDefault) const; + + bool + SetFormatValue (lldb::Format new_value); + + const RegularExpression * + GetRegexValue () const; + + int64_t + GetSInt64Value (int64_t fail_value = 0) const; + + bool + SetSInt64Value (int64_t new_value); + + const char * + GetStringValue (const char *fail_value = NULL) const; + + bool + SetStringValue (const char *new_value); + + uint64_t + GetUInt64Value (uint64_t fail_value = 0) const; + + bool + SetUInt64Value (uint64_t new_value); + + UUID + GetUUIDValue () const; + + bool + SetUUIDValue (const UUID &uuid); + + bool + OptionWasSet () const + { + return m_value_was_set; + } + + void + SetOptionWasSet () + { + m_value_was_set = true; + } + + void + SetParent (const lldb::OptionValueSP &parent_sp) + { + m_parent_wp = parent_sp; + } + protected: + lldb::OptionValueWP m_parent_wp; + bool m_value_was_set; // This can be used to see if a value has been set + // by a call to SetValueFromCString(). It is often + // handy to know if an option value was set from + // the command line or as a setting, versus if we + // just have the default value that was already + // populated in the option value. + + }; + +} // namespace lldb_private + +#endif // liblldb_OptionValue_h_ diff --git a/include/lldb/Interpreter/OptionValueArch.h b/include/lldb/Interpreter/OptionValueArch.h new file mode 100644 index 00000000000..662e1ec9f62 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueArch.h @@ -0,0 +1,139 @@ +//===-- OptionValueArch.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueArch_h_ +#define liblldb_OptionValueArch_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueArch : public OptionValue +{ +public: + OptionValueArch () : + OptionValue(), + m_current_value (), + m_default_value () + { + } + + OptionValueArch (const char *triple) : + OptionValue(), + m_current_value (triple), + m_default_value () + { + m_default_value = m_current_value; + } + + OptionValueArch (const ArchSpec &value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + + OptionValueArch (const ArchSpec ¤t_value, + const ArchSpec &default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueArch() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeArch; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + ArchSpec & + GetCurrentValue() + { + return m_current_value; + } + + const ArchSpec & + GetCurrentValue() const + { + return m_current_value; + } + + const ArchSpec & + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (const ArchSpec &value, bool set_value_was_set) + { + m_current_value = value; + if (set_value_was_set) + m_value_was_set = true; + } + + void + SetDefaultValue (const ArchSpec &value) + { + m_default_value = value; + } + +protected: + ArchSpec m_current_value; + ArchSpec m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueArch_h_ diff --git a/include/lldb/Interpreter/OptionValueArgs.h b/include/lldb/Interpreter/OptionValueArgs.h new file mode 100644 index 00000000000..365a52a8b39 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueArgs.h @@ -0,0 +1,46 @@ +//===-- OptionValueArgs.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueArgs_h_ +#define liblldb_OptionValueArgs_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValueArray.h" + +namespace lldb_private { + +class OptionValueArgs : public OptionValueArray +{ +public: + OptionValueArgs () : + OptionValueArray (OptionValue::ConvertTypeToMask (OptionValue::eTypeString)) + { + } + + virtual + ~OptionValueArgs() + { + } + + size_t + GetArgs (Args &args); + + virtual Type + GetType() const + { + return eTypeArgs; + } +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueArgs_h_ diff --git a/include/lldb/Interpreter/OptionValueArray.h b/include/lldb/Interpreter/OptionValueArray.h new file mode 100644 index 00000000000..39ae2f6f43d --- /dev/null +++ b/include/lldb/Interpreter/OptionValueArray.h @@ -0,0 +1,178 @@ +//===-- OptionValueArray.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueArray_h_ +#define liblldb_OptionValueArray_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueArray : public OptionValue +{ +public: + OptionValueArray (uint32_t type_mask = UINT32_MAX, bool raw_value_dump = false) : + m_type_mask (type_mask), + m_values (), + m_raw_value_dump(raw_value_dump) + { + } + + virtual + ~OptionValueArray() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeArray; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_values.clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + virtual lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + size_t + GetSize () const + { + return m_values.size(); + } + + lldb::OptionValueSP + operator[](size_t idx) const + { + lldb::OptionValueSP value_sp; + if (idx < m_values.size()) + value_sp = m_values[idx]; + return value_sp; + } + + lldb::OptionValueSP + GetValueAtIndex (size_t idx) const + { + lldb::OptionValueSP value_sp; + if (idx < m_values.size()) + value_sp = m_values[idx]; + return value_sp; + } + + bool + AppendValue (const lldb::OptionValueSP &value_sp) + { + // 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())) + { + m_values.push_back(value_sp); + return true; + } + return false; + } + + bool + InsertValue (size_t idx, const lldb::OptionValueSP &value_sp) + { + // 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 (idx < m_values.size()) + m_values.insert(m_values.begin() + idx, value_sp); + else + m_values.push_back(value_sp); + return true; + } + return false; + } + + bool + ReplaceValue (size_t idx, const lldb::OptionValueSP &value_sp) + { + // 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 (idx < m_values.size()) + { + m_values[idx] = value_sp; + return true; + } + } + return false; + } + + bool + DeleteValue (size_t idx) + { + if (idx < m_values.size()) + { + m_values.erase (m_values.begin() + idx); + return true; + } + return false; + } + + size_t + GetArgs (Args &args) const; + + Error + SetArgs (const Args &args, VarSetOperationType op); + +protected: + typedef std::vector collection; + + uint32_t m_type_mask; + collection m_values; + bool m_raw_value_dump; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueArray_h_ diff --git a/include/lldb/Interpreter/OptionValueBoolean.h b/include/lldb/Interpreter/OptionValueBoolean.h new file mode 100644 index 00000000000..2b935e9e03e --- /dev/null +++ b/include/lldb/Interpreter/OptionValueBoolean.h @@ -0,0 +1,141 @@ +//===-- OptionValueBoolean.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueBoolean_h_ +#define liblldb_OptionValueBoolean_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueBoolean : public OptionValue +{ +public: + OptionValueBoolean (bool value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + OptionValueBoolean (bool current_value, + bool default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueBoolean() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeBoolean; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + //------------------------------------------------------------------ + /// Convert to bool operator. + /// + /// This allows code to check a OptionValueBoolean in conditions. + /// + /// @code + /// OptionValueBoolean bool_value(...); + /// if (bool_value) + /// { ... + /// @endcode + /// + /// @return + /// /b True this object contains a valid namespace decl, \b + /// false otherwise. + //------------------------------------------------------------------ + operator bool() const + { + return m_current_value; + } + + const bool & + operator = (bool b) + { + m_current_value = b; + return m_current_value; + } + + bool + GetCurrentValue() const + { + return m_current_value; + } + + bool + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (bool value) + { + m_current_value = value; + } + + void + SetDefaultValue (bool value) + { + m_default_value = value; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + +protected: + bool m_current_value; + bool m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueBoolean_h_ diff --git a/include/lldb/Interpreter/OptionValueDictionary.h b/include/lldb/Interpreter/OptionValueDictionary.h new file mode 100644 index 00000000000..5fb698b9f22 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueDictionary.h @@ -0,0 +1,139 @@ +//===-- OptionValueDictionary.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueDictionary_h_ +#define liblldb_OptionValueDictionary_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueDictionary : public OptionValue +{ +public: + OptionValueDictionary (uint32_t type_mask = UINT32_MAX, bool raw_value_dump = true) : + OptionValue(), + m_type_mask (type_mask), + m_values (), + m_raw_value_dump (raw_value_dump) + { + } + + virtual + ~OptionValueDictionary() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeDictionary; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_values.clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + bool + IsHomogenous() const + { + return ConvertTypeMaskToType (m_type_mask) != eTypeInvalid; + } + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + size_t + GetNumValues() const + { + return m_values.size(); + } + + lldb::OptionValueSP + GetValueForKey (const ConstString &key) const; + + virtual lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const; + + virtual Error + SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value); + + //--------------------------------------------------------------------- + // String value getters and setters + //--------------------------------------------------------------------- + const char * + GetStringValueForKey (const ConstString &key); + + bool + SetStringValueForKey (const ConstString &key, + const char *value, + bool can_replace = true); + + + bool + SetValueForKey (const ConstString &key, + const lldb::OptionValueSP &value_sp, + bool can_replace = true); + + bool + DeleteValueForKey (const ConstString &key); + + size_t + GetArgs (Args &args) const; + + Error + SetArgs (const Args &args, VarSetOperationType op); + +protected: + typedef std::map collection; + uint32_t m_type_mask; + collection m_values; + bool m_raw_value_dump; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueDictionary_h_ diff --git a/include/lldb/Interpreter/OptionValueEnumeration.h b/include/lldb/Interpreter/OptionValueEnumeration.h new file mode 100644 index 00000000000..012eeb68ac0 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueEnumeration.h @@ -0,0 +1,126 @@ +//===-- OptionValueEnumeration.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueEnumeration_h_ +#define liblldb_OptionValueEnumeration_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + + +class OptionValueEnumeration : public OptionValue +{ +public: + typedef int64_t enum_type; + struct EnumeratorInfo + { + enum_type value; + const char *description; + }; + typedef UniqueCStringMap EnumerationMap; + typedef typename EnumerationMap::Entry EnumerationMapEntry; + + OptionValueEnumeration (const OptionEnumValueElement *enumerators, enum_type value); + + virtual + ~OptionValueEnumeration(); + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeEnum; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + enum_type + operator = (enum_type value) + { + m_current_value = value; + return m_current_value; + } + + enum_type + GetCurrentValue() const + { + return m_current_value; + } + + enum_type + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (enum_type value) + { + m_current_value = value; + } + + void + SetDefaultValue (enum_type value) + { + m_default_value = value; + } + +protected: + void + SetEnumerations (const OptionEnumValueElement *enumerators); + + enum_type m_current_value; + enum_type m_default_value; + EnumerationMap m_enumerations; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueEnumeration_h_ diff --git a/include/lldb/Interpreter/OptionValueFileSpec.h b/include/lldb/Interpreter/OptionValueFileSpec.h new file mode 100644 index 00000000000..7e74b605660 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueFileSpec.h @@ -0,0 +1,129 @@ +//===-- OptionValueFileSpec.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueFileSpec_h_ +#define liblldb_OptionValueFileSpec_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueFileSpec : public OptionValue +{ +public: + OptionValueFileSpec (); + + OptionValueFileSpec (const FileSpec &value); + + OptionValueFileSpec (const FileSpec ¤t_value, + const FileSpec &default_value); + + virtual + ~OptionValueFileSpec() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeFileSpec; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + m_data_sp.reset(); + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + FileSpec & + GetCurrentValue() + { + return m_current_value; + } + + const FileSpec & + GetCurrentValue() const + { + return m_current_value; + } + + const FileSpec & + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (const FileSpec &value, bool set_value_was_set) + { + m_current_value = value; + if (set_value_was_set) + m_value_was_set = true; + m_data_sp.reset(); + } + + void + SetDefaultValue (const FileSpec &value) + { + m_default_value = value; + } + + const lldb::DataBufferSP & + GetFileContents(bool null_terminate); + + void + SetCompletionMask (uint32_t mask) + { + m_completion_mask = mask; + } + +protected: + FileSpec m_current_value; + FileSpec m_default_value; + lldb::DataBufferSP m_data_sp; + uint32_t m_completion_mask; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueFileSpec_h_ diff --git a/include/lldb/Interpreter/OptionValueFileSpecList.h b/include/lldb/Interpreter/OptionValueFileSpecList.h new file mode 100644 index 00000000000..792de4e23af --- /dev/null +++ b/include/lldb/Interpreter/OptionValueFileSpecList.h @@ -0,0 +1,105 @@ +//===-- OptionValueFileSpecList.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueFileSpecList_h_ +#define liblldb_OptionValueFileSpecList_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/FileSpecList.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueFileSpecList : public OptionValue +{ +public: + OptionValueFileSpecList () : + OptionValue(), + m_current_value () + { + } + + OptionValueFileSpecList (const FileSpecList ¤t_value) : + OptionValue(), + m_current_value (current_value) + { + } + + + virtual + ~OptionValueFileSpecList() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeFileSpecList; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value.Clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + FileSpecList & + GetCurrentValue() + { + return m_current_value; + } + + const FileSpecList & + GetCurrentValue() const + { + return m_current_value; + } + + void + SetCurrentValue (const FileSpecList &value) + { + m_current_value = value; + } + +protected: + FileSpecList m_current_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueFileSpecList_h_ diff --git a/include/lldb/Interpreter/OptionValueFormat.h b/include/lldb/Interpreter/OptionValueFormat.h new file mode 100644 index 00000000000..245b2eeb5af --- /dev/null +++ b/include/lldb/Interpreter/OptionValueFormat.h @@ -0,0 +1,107 @@ +//===-- OptionValueFormat.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueFormat_h_ +#define liblldb_OptionValueFormat_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueFormat : public OptionValue +{ +public: + OptionValueFormat (lldb::Format value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + + OptionValueFormat (lldb::Format current_value, + lldb::Format default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueFormat() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeFormat; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + lldb::Format + GetCurrentValue() const + { + return m_current_value; + } + + lldb::Format + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (lldb::Format value) + { + m_current_value = value; + } + + void + SetDefaultValue (lldb::Format value) + { + m_default_value = value; + } + +protected: + lldb::Format m_current_value; + lldb::Format m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueFormat_h_ diff --git a/include/lldb/Interpreter/OptionValuePathMappings.h b/include/lldb/Interpreter/OptionValuePathMappings.h new file mode 100644 index 00000000000..7ebf4947c6a --- /dev/null +++ b/include/lldb/Interpreter/OptionValuePathMappings.h @@ -0,0 +1,94 @@ +//===-- OptionValuePathMappings.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValuePathMappings_h_ +#define liblldb_OptionValuePathMappings_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/PathMappingList.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValuePathMappings : public OptionValue +{ +public: + OptionValuePathMappings (bool notify_changes) : + OptionValue(), + m_path_mappings (), + m_notify_changes (notify_changes) + { + } + + virtual + ~OptionValuePathMappings() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypePathMap; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_path_mappings.Clear(m_notify_changes); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual bool + IsAggregateValue () const + { + return true; + } + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + PathMappingList & + GetCurrentValue() + { + return m_path_mappings; + } + + const PathMappingList & + GetCurrentValue() const + { + return m_path_mappings; + } + +protected: + PathMappingList m_path_mappings; + bool m_notify_changes; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValuePathMappings_h_ diff --git a/include/lldb/Interpreter/OptionValueProperties.h b/include/lldb/Interpreter/OptionValueProperties.h new file mode 100644 index 00000000000..0024f20e2ff --- /dev/null +++ b/include/lldb/Interpreter/OptionValueProperties.h @@ -0,0 +1,265 @@ +//===-- OptionValueProperties.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueProperties_h_ +#define liblldb_OptionValueProperties_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/Property.h" + +namespace lldb_private { + +class OptionValueProperties : + public OptionValue, + public std::enable_shared_from_this +{ +public: + + //--------------------------------------------------------------------- + // OptionValueProperties + //--------------------------------------------------------------------- + OptionValueProperties () : + OptionValue(), + m_name (), + m_properties (), + m_name_to_index () + { + } + + OptionValueProperties (const ConstString &name); + + OptionValueProperties (const OptionValueProperties &global_properties); + + virtual + ~OptionValueProperties() + { + } + + virtual Type + GetType () const + { + return eTypeProperties; + } + + virtual bool + Clear (); + + virtual lldb::OptionValueSP + DeepCopy () const; + + virtual Error + SetValueFromCString (const char *value, VarSetOperationType op = eVarSetOperationAssign); + + virtual void + DumpValue (const ExecutionContext *exe_ctx, + Stream &strm, + uint32_t dump_mask); + + virtual ConstString + GetName () const + { + return m_name; + } + + virtual Error + DumpPropertyValue (const ExecutionContext *exe_ctx, + Stream &strm, + const char *property_path, + uint32_t dump_mask); + + virtual void + DumpAllDescriptions (CommandInterpreter &interpreter, + Stream &strm) const; + + void + Apropos (const char *keyword, + std::vector &matching_properties) const; + + void + Initialize (const PropertyDefinition *setting_definitions); + +// bool +// GetQualifiedName (Stream &strm); + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + virtual size_t + GetNumProperties() const; + + virtual ConstString + GetPropertyNameAtIndex (uint32_t idx) const; + + virtual const char * + GetPropertyDescriptionAtIndex (uint32_t idx) const; + + //--------------------------------------------------------------------- + // Get the index of a property given its exact name in this property + // collection, "name" can't be a path to a property path that refers + // to a property within a property + //--------------------------------------------------------------------- + virtual uint32_t + GetPropertyIndex (const ConstString &name) const; + + //--------------------------------------------------------------------- + // Get a property by exact name exists in this property collection, name + // can not be a path to a property path that refers to a property within + // a property + //--------------------------------------------------------------------- + virtual const Property * + GetProperty (const ExecutionContext *exe_ctx, + bool will_modify, + const ConstString &name) const; + + virtual const Property * + GetPropertyAtIndex (const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const; + + //--------------------------------------------------------------------- + // Property can be be a property path like "target.process.extra-startup-command" + //--------------------------------------------------------------------- + virtual const Property * + GetPropertyAtPath (const ExecutionContext *exe_ctx, + bool will_modify, + const char *property_path) const; + + virtual lldb::OptionValueSP + GetPropertyValueAtIndex (const ExecutionContext *exe_ctx, + bool will_modify, + uint32_t idx) const; + + virtual lldb::OptionValueSP + GetValueForKey (const ExecutionContext *exe_ctx, + const ConstString &key, + bool value_will_be_modified) const; + + lldb::OptionValueSP + GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool value_will_be_modified, + Error &error) const; + + virtual Error + SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *path, + const char *value); + + virtual bool + PredicateMatches (const ExecutionContext *exe_ctx, + const char *predicate) const + { + return false; + } + + + OptionValueArch * + GetPropertyAtIndexAsOptionValueArch (const ExecutionContext *exe_ctx, uint32_t idx) const; + + bool + GetPropertyAtIndexAsArgs (const ExecutionContext *exe_ctx, uint32_t idx, Args &args) const; + + bool + SetPropertyAtIndexFromArgs (const ExecutionContext *exe_ctx, uint32_t idx, const Args &args); + + bool + GetPropertyAtIndexAsBoolean (const ExecutionContext *exe_ctx, uint32_t idx, bool fail_value) const; + + bool + SetPropertyAtIndexAsBoolean (const ExecutionContext *exe_ctx, uint32_t idx, bool new_value); + + OptionValueDictionary * + GetPropertyAtIndexAsOptionValueDictionary (const ExecutionContext *exe_ctx, uint32_t idx) const; + + int64_t + GetPropertyAtIndexAsEnumeration (const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const; + + bool + SetPropertyAtIndexAsEnumeration (const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value); + + const RegularExpression * + GetPropertyAtIndexAsOptionValueRegex (const ExecutionContext *exe_ctx, uint32_t idx) const; + + OptionValueSInt64 * + GetPropertyAtIndexAsOptionValueSInt64 (const ExecutionContext *exe_ctx, uint32_t idx) const; + + int64_t + GetPropertyAtIndexAsSInt64 (const ExecutionContext *exe_ctx, uint32_t idx, int64_t fail_value) const; + + bool + SetPropertyAtIndexAsSInt64 (const ExecutionContext *exe_ctx, uint32_t idx, int64_t new_value); + + uint64_t + GetPropertyAtIndexAsUInt64 (const ExecutionContext *exe_ctx, uint32_t idx, uint64_t fail_value) const; + + bool + SetPropertyAtIndexAsUInt64 (const ExecutionContext *exe_ctx, uint32_t idx, uint64_t new_value); + + const char * + GetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *fail_value) const; + + bool + SetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *new_value); + + OptionValueString * + GetPropertyAtIndexAsOptionValueString (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + OptionValueFileSpec * + GetPropertyAtIndexAsOptionValueFileSpec (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + FileSpec + GetPropertyAtIndexAsFileSpec (const ExecutionContext *exe_ctx, uint32_t idx) const; + + bool + SetPropertyAtIndexAsFileSpec (const ExecutionContext *exe_ctx, uint32_t idx, const FileSpec &file_spec); + + OptionValuePathMappings * + GetPropertyAtIndexAsOptionValuePathMappings (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + OptionValueFileSpecList * + GetPropertyAtIndexAsOptionValueFileSpecList (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const; + + void + AppendProperty(const ConstString &name, + const ConstString &desc, + bool is_global, + const lldb::OptionValueSP &value_sp); + + lldb::OptionValuePropertiesSP + GetSubProperty (const ExecutionContext *exe_ctx, + const ConstString &name); + +protected: + + const Property * + ProtectedGetPropertyAtIndex (uint32_t idx) const + { + if (idx < m_properties.size()) + return &m_properties[idx]; + return NULL; + } + + typedef UniqueCStringMap NameToIndex; + + ConstString m_name; + std::vector m_properties; + NameToIndex m_name_to_index; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueProperties_h_ diff --git a/include/lldb/Interpreter/OptionValueRegex.h b/include/lldb/Interpreter/OptionValueRegex.h new file mode 100644 index 00000000000..bb8c4588e22 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueRegex.h @@ -0,0 +1,98 @@ +//===-- OptionValueRegex.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueRegex_h_ +#define liblldb_OptionValueRegex_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/RegularExpression.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueRegex : public OptionValue +{ +public: + OptionValueRegex (const char *value = NULL, uint32_t regex_flags = 0) : + OptionValue(), + m_regex (value, regex_flags) + { + } + + virtual + ~OptionValueRegex() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeRegex; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_regex.Clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + const RegularExpression * + GetCurrentValue() const + { + if (m_regex.IsValid()) + return &m_regex; + return NULL; + } + + void + SetCurrentValue (const char *value, uint32_t regex_flags) + { + if (value && value[0]) + m_regex.Compile (value, regex_flags); + else + m_regex.Clear(); + } + + bool + IsValid () const + { + return m_regex.IsValid(); + } + +protected: + RegularExpression m_regex; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueRegex_h_ diff --git a/include/lldb/Interpreter/OptionValueSInt64.h b/include/lldb/Interpreter/OptionValueSInt64.h new file mode 100644 index 00000000000..8bc8fb2da2d --- /dev/null +++ b/include/lldb/Interpreter/OptionValueSInt64.h @@ -0,0 +1,172 @@ +//===-- OptionValueSInt64.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueSInt64_h_ +#define liblldb_OptionValueSInt64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueSInt64 : public OptionValue +{ +public: + OptionValueSInt64 () : + OptionValue(), + m_current_value (0), + m_default_value (0), + m_min_value (INT64_MIN), + m_max_value (INT64_MAX) + { + } + + OptionValueSInt64 (int64_t value) : + OptionValue(), + m_current_value (value), + m_default_value (value), + m_min_value (INT64_MIN), + m_max_value (INT64_MAX) + { + } + + OptionValueSInt64 (int64_t current_value, + int64_t default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value), + m_min_value (INT64_MIN), + m_max_value (INT64_MAX) + { + } + + OptionValueSInt64 (const OptionValueSInt64 &rhs) : + OptionValue(rhs), + m_current_value (rhs.m_current_value), + m_default_value (rhs.m_default_value), + m_min_value (rhs.m_min_value), + m_max_value (rhs.m_max_value) + { + } + + virtual + ~OptionValueSInt64() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeSInt64; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + const int64_t & + operator = (int64_t value) + { + m_current_value = value; + return m_current_value; + } + + int64_t + GetCurrentValue() const + { + return m_current_value; + } + + int64_t + GetDefaultValue() const + { + return m_default_value; + } + + bool + SetCurrentValue (int64_t value) + { + if (value >= m_min_value && value <= m_max_value) + { + m_current_value = value; + return true; + } + return false; + } + + bool + SetDefaultValue (int64_t value) + { + if (value >= m_min_value && value <= m_max_value) + { + m_default_value = value; + return true; + } + return false; + } + + void + SetMinimumValue (int64_t v) + { + m_min_value = v; + } + + int64_t + GetMinimumValue () const + { + return m_min_value; + } + + void + SetMaximumValue (int64_t v) + { + m_max_value = v; + } + + int64_t + GetMaximumValue () const + { + return m_max_value; + } + +protected: + int64_t m_current_value; + int64_t m_default_value; + int64_t m_min_value; + int64_t m_max_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueSInt64_h_ diff --git a/include/lldb/Interpreter/OptionValueString.h b/include/lldb/Interpreter/OptionValueString.h new file mode 100644 index 00000000000..a82e1403b74 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueString.h @@ -0,0 +1,227 @@ +//===-- OptionValueString.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueString_h_ +#define liblldb_OptionValueString_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Flags.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueString : public OptionValue +{ +public: + + typedef Error (*ValidatorCallback) (const char* string, + void* baton); + + enum Options + { + eOptionEncodeCharacterEscapeSequences = (1u << 0) + }; + + OptionValueString () : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(), + m_validator_baton() + { + } + + OptionValueString (ValidatorCallback validator, + void* baton = NULL) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(validator), + m_validator_baton(baton) + { + } + + OptionValueString (const char *value) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(), + m_validator_baton() + { + if (value && value[0]) + { + m_current_value.assign (value); + m_default_value.assign (value); + } + } + + OptionValueString (const char *current_value, + const char *default_value) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(), + m_validator_baton() + { + if (current_value && current_value[0]) + m_current_value.assign (current_value); + if (default_value && default_value[0]) + m_default_value.assign (default_value); + } + + OptionValueString (const char *value, + ValidatorCallback validator, + void* baton = NULL) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(validator), + m_validator_baton(baton) + { + if (value && value[0]) + { + m_current_value.assign (value); + m_default_value.assign (value); + } + } + + OptionValueString (const char *current_value, + const char *default_value, + ValidatorCallback validator, + void* baton = NULL) : + OptionValue(), + m_current_value (), + m_default_value (), + m_options(), + m_validator(validator), + m_validator_baton(baton) + { + if (current_value && current_value[0]) + m_current_value.assign (current_value); + if (default_value && default_value[0]) + m_default_value.assign (default_value); + } + + virtual + ~OptionValueString() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeString; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + Flags & + GetOptions () + { + return m_options; + } + + const Flags & + GetOptions () const + { + return m_options; + } + + const char * + operator = (const char *value) + { + SetCurrentValue(value); + return m_current_value.c_str(); + } + + const char * + GetCurrentValue() const + { + return m_current_value.c_str(); + } + + const char * + GetDefaultValue() const + { + return m_default_value.c_str(); + } + + Error + SetCurrentValue (const char *value); + + Error + AppendToCurrentValue (const char *value); + + void + SetDefaultValue (const char *value) + { + if (value && value[0]) + m_default_value.assign (value); + else + m_default_value.clear(); + } + + bool + IsCurrentValueEmpty () const + { + return m_current_value.empty(); + } + + bool + IsDefaultValueEmpty () const + { + return m_default_value.empty(); + } + + +protected: + std::string m_current_value; + std::string m_default_value; + Flags m_options; + ValidatorCallback m_validator; + void* m_validator_baton; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueString_h_ diff --git a/include/lldb/Interpreter/OptionValueUInt64.h b/include/lldb/Interpreter/OptionValueUInt64.h new file mode 100644 index 00000000000..9b5496f9835 --- /dev/null +++ b/include/lldb/Interpreter/OptionValueUInt64.h @@ -0,0 +1,134 @@ +//===-- OptionValueUInt64.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueUInt64_h_ +#define liblldb_OptionValueUInt64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueUInt64 : public OptionValue +{ +public: + OptionValueUInt64 () : + OptionValue(), + m_current_value (0), + m_default_value (0) + { + } + + OptionValueUInt64 (uint64_t value) : + OptionValue(), + m_current_value (value), + m_default_value (value) + { + } + + OptionValueUInt64 (uint64_t current_value, + uint64_t default_value) : + OptionValue(), + m_current_value (current_value), + m_default_value (default_value) + { + } + + virtual + ~OptionValueUInt64() + { + } + + //--------------------------------------------------------------------- + // Decode a uint64_t from "value_cstr" return a OptionValueUInt64 object + // inside of a lldb::OptionValueSP object if all goes well. If the + // string isn't a uint64_t value or any other error occurs, return an + // empty lldb::OptionValueSP and fill error in with the correct stuff. + //--------------------------------------------------------------------- + static lldb::OptionValueSP + Create (const char *value_cstr, Error &error); + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeUInt64; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_current_value = m_default_value; + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + const uint64_t & + operator = (uint64_t value) + { + m_current_value = value; + return m_current_value; + } + + operator uint64_t () const + { + return m_current_value; + } + + uint64_t + GetCurrentValue() const + { + return m_current_value; + } + + uint64_t + GetDefaultValue() const + { + return m_default_value; + } + + void + SetCurrentValue (uint64_t value) + { + m_current_value = value; + } + + void + SetDefaultValue (uint64_t value) + { + m_default_value = value; + } + +protected: + uint64_t m_current_value; + uint64_t m_default_value; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueUInt64_h_ diff --git a/include/lldb/Interpreter/OptionValueUUID.h b/include/lldb/Interpreter/OptionValueUUID.h new file mode 100644 index 00000000000..caf436e576f --- /dev/null +++ b/include/lldb/Interpreter/OptionValueUUID.h @@ -0,0 +1,106 @@ +//===-- OptionValueUUID.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValueUUID_h_ +#define liblldb_OptionValueUUID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/UUID.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + +class OptionValueUUID : public OptionValue +{ +public: + OptionValueUUID () : + OptionValue(), + m_uuid () + { + } + + OptionValueUUID (const UUID &uuid) : + OptionValue(), + m_uuid (uuid) + { + } + + virtual + ~OptionValueUUID() + { + } + + //--------------------------------------------------------------------- + // Virtual subclass pure virtual overrides + //--------------------------------------------------------------------- + + virtual OptionValue::Type + GetType () const + { + return eTypeUUID; + } + + virtual void + DumpValue (const ExecutionContext *exe_ctx, Stream &strm, uint32_t dump_mask); + + virtual Error + SetValueFromCString (const char *value, + VarSetOperationType op = eVarSetOperationAssign); + + virtual bool + Clear () + { + m_uuid.Clear(); + m_value_was_set = false; + return true; + } + + virtual lldb::OptionValueSP + DeepCopy () const; + + //--------------------------------------------------------------------- + // Subclass specific functions + //--------------------------------------------------------------------- + + UUID & + GetCurrentValue() + { + return m_uuid; + } + + const UUID & + GetCurrentValue() const + { + return m_uuid; + } + + void + SetCurrentValue (const UUID &value) + { + m_uuid = value; + } + + virtual size_t + AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + +protected: + UUID m_uuid; +}; + +} // namespace lldb_private + +#endif // liblldb_OptionValueUUID_h_ diff --git a/include/lldb/Interpreter/OptionValues.h b/include/lldb/Interpreter/OptionValues.h new file mode 100644 index 00000000000..41b9d2e351f --- /dev/null +++ b/include/lldb/Interpreter/OptionValues.h @@ -0,0 +1,31 @@ +//===-- OptionValues.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OptionValues_h_ +#define liblldb_OptionValues_h_ + +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/OptionValueArch.h" +#include "lldb/Interpreter/OptionValueArgs.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" +#include "lldb/Interpreter/OptionValueFileSpecList.h" +#include "lldb/Interpreter/OptionValueFormat.h" +#include "lldb/Interpreter/OptionValuePathMappings.h" +#include "lldb/Interpreter/OptionValueProperties.h" +#include "lldb/Interpreter/OptionValueRegex.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/OptionValueUUID.h" + +#endif // liblldb_OptionValues_h_ diff --git a/include/lldb/Interpreter/Options.h b/include/lldb/Interpreter/Options.h new file mode 100644 index 00000000000..ac4daa8f579 --- /dev/null +++ b/include/lldb/Interpreter/Options.h @@ -0,0 +1,487 @@ +//===-- Options.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Options_h_ +#define liblldb_Options_h_ + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/lldb-defines.h" +#include "lldb/Interpreter/Args.h" + +namespace lldb_private { + + static inline bool + isprint8 (int ch) + { + if (ch & 0xffffff00u) + return false; + return isprint(ch); + } + + +//---------------------------------------------------------------------- +/// @class Options Options.h "lldb/Interpreter/Options.h" +/// @brief A command line option parsing protocol class. +/// +/// Options is designed to be subclassed to contain all needed +/// options for a given command. The options can be parsed by calling: +/// \code +/// Error Args::ParseOptions (Options &); +/// \endcode +/// +/// The options are specified using the format defined for the libc +/// options parsing function getopt_long_only: +/// \code +/// #include +/// int getopt_long_only(int argc, char * const *argv, const char *optstring, const struct option *longopts, int *longindex); +/// \endcode +/// +/// Example code: +/// \code +/// #include +/// #include +/// +/// class CommandOptions : public Options +/// { +/// public: +/// virtual struct option * +/// GetLongOptions() { +/// return g_options; +/// } +/// +/// virtual Error +/// SetOptionValue (uint32_t option_idx, int option_val, const char *option_arg) +/// { +/// Error error; +/// switch (option_val) +/// { +/// case 'g': debug = true; break; +/// case 'v': verbose = true; break; +/// case 'l': log_file = option_arg; break; +/// case 'f': log_flags = strtoull(option_arg, NULL, 0); break; +/// default: +/// error.SetErrorStringWithFormat("unrecognized short option %c", option_val); +/// break; +/// } +/// +/// return error; +/// } +/// +/// CommandOptions (CommandInterpreter &interpreter) : debug (true), verbose (false), log_file (), log_flags (0) +/// {} +/// +/// bool debug; +/// bool verbose; +/// std::string log_file; +/// uint32_t log_flags; +/// +/// static struct option g_options[]; +/// +/// }; +/// +/// struct option CommandOptions::g_options[] = +/// { +/// { "debug", no_argument, NULL, 'g' }, +/// { "log-file", required_argument, NULL, 'l' }, +/// { "log-flags", required_argument, NULL, 'f' }, +/// { "verbose", no_argument, NULL, 'v' }, +/// { NULL, 0, NULL, 0 } +/// }; +/// +/// int main (int argc, const char **argv, const char **envp) +/// { +/// CommandOptions options; +/// Args main_command; +/// main_command.SetArguments(argc, argv, false); +/// main_command.ParseOptions(options); +/// +/// if (options.verbose) +/// { +/// std::cout << "verbose is on" << std::endl; +/// } +/// } +/// \endcode +//---------------------------------------------------------------------- +class Options +{ +public: + + Options (CommandInterpreter &interpreter); + + virtual + ~Options (); + + void + BuildGetoptTable (); + + void + BuildValidOptionSets (); + + uint32_t + NumCommandOptions (); + + //------------------------------------------------------------------ + /// Get the option definitions to use when parsing Args options. + /// + /// @see Args::ParseOptions (Options&) + /// @see man getopt_long_only + //------------------------------------------------------------------ + struct option * + GetLongOptions (); + + // This gets passed the short option as an integer... + void + OptionSeen (int short_option); + + bool + VerifyOptions (CommandReturnObject &result); + + // Verify that the options given are in the options table and can + // be used together, but there may be some required options that are + // missing (used to verify options that get folded into command aliases). + + bool + VerifyPartialOptions (CommandReturnObject &result); + + void + OutputFormattedUsageText (Stream &strm, + const char *text, + uint32_t output_max_columns); + + void + GenerateOptionUsage (Stream &strm, + CommandObject *cmd); + + bool + SupportsLongOption (const char *long_option); + + // The following two pure virtual functions must be defined by every + // class that inherits from this class. + + virtual const OptionDefinition* + GetDefinitions () { return NULL; } + + // Call this prior to parsing any options. This call will call the + // subclass OptionParsingStarting() and will avoid the need for all + // OptionParsingStarting() function instances from having to call the + // Option::OptionParsingStarting() like they did before. This was error + // prone and subclasses shouldn't have to do it. + void + NotifyOptionParsingStarting (); + + Error + NotifyOptionParsingFinished (); + + //------------------------------------------------------------------ + /// Set the value of an option. + /// + /// @param[in] option_idx + /// The index into the "struct option" array that was returned + /// by Options::GetLongOptions(). + /// + /// @param[in] option_arg + /// The argument value for the option that the user entered, or + /// NULL if there is no argument for the current option. + /// + /// + /// @see Args::ParseOptions (Options&) + /// @see man getopt_long_only + //------------------------------------------------------------------ + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) = 0; + + //------------------------------------------------------------------ + /// Handles the generic bits of figuring out whether we are in an + /// option, and if so completing it. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// See CommandObject::HandleCompletions for a description of + /// how these work. + /// + /// @param[in] interpreter + /// The interpreter that's doing the completing. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will be + /// inserted after the completion.) \b false otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to + /// make a distinction between total number of matches, and the + /// window the user wants returned. + /// + /// @return + /// \btrue if we were in an option, \bfalse otherwise. + //------------------------------------------------------------------ + bool + HandleOptionCompletion (Args &input, + OptionElementVector &option_map, + int cursor_index, + int char_pos, + int match_start_point, + int max_return_elements, + bool &word_complete, + lldb_private::StringList &matches); + + //------------------------------------------------------------------ + /// Handles the generic bits of figuring out whether we are in an + /// option, and if so completing it. + /// + /// @param[in] interpreter + /// The command interpreter doing the completion. + /// + /// @param[in] input + /// The command line parsed into words + /// + /// @param[in] cursor_index + /// The index in \ainput of the word in which the cursor lies. + /// + /// @param[in] char_pos + /// The character position of the cursor in its argument word. + /// + /// @param[in] opt_element_vector + /// The results of the options parse of \a input. + /// + /// @param[in] opt_element_index + /// The position in \a opt_element_vector of the word in \a + /// input containing the cursor. + /// + /// @param[in] match_start_point + /// @param[in] match_return_elements + /// See CommandObject::HandleCompletions for a description of + /// how these work. + /// + /// @param[out] word_complete + /// \btrue if this is a complete option value (a space will + /// be inserted after the completion.) \bfalse otherwise. + /// + /// @param[out] matches + /// The array of matches returned. + /// + /// FIXME: This is the wrong return value, since we also need to + /// make a distinction between total number of matches, and the + /// window the user wants returned. + /// + /// @return + /// \btrue if we were in an option, \bfalse otherwise. + //------------------------------------------------------------------ + virtual bool + HandleOptionArgumentCompletion (Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + +protected: + // This is a set of options expressed as indexes into the options table for this Option. + typedef std::set OptionSet; + typedef std::vector OptionSetVector; + + CommandInterpreter &m_interpreter; + std::vector m_getopt_table; + OptionSet m_seen_options; + OptionSetVector m_required_options; + OptionSetVector m_optional_options; + + OptionSetVector &GetRequiredOptions () + { + BuildValidOptionSets(); + return m_required_options; + } + + OptionSetVector &GetOptionalOptions () + { + BuildValidOptionSets(); + return m_optional_options; + } + + bool + IsASubset (const OptionSet& set_a, const OptionSet& set_b); + + size_t + OptionsSetDiff (const OptionSet &set_a, const OptionSet &set_b, OptionSet &diffs); + + void + OptionsSetUnion (const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set); + + // Subclasses must reset their option values prior to starting a new + // option parse. Each subclass must override this function and revert + // all option settings to default values. + virtual void + OptionParsingStarting () = 0; + + virtual Error + OptionParsingFinished () + { + // If subclasses need to know when the options are done being parsed + // they can implement this function to do extra checking + Error error; + return error; + } +}; + + class OptionGroup + { + public: + OptionGroup () + { + } + + virtual + ~OptionGroup () + { + } + + virtual uint32_t + GetNumDefinitions () = 0; + + virtual const OptionDefinition* + GetDefinitions () = 0; + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) = 0; + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) = 0; + + virtual Error + OptionParsingFinished (CommandInterpreter &interpreter) + { + // If subclasses need to know when the options are done being parsed + // they can implement this function to do extra checking + Error error; + return error; + } + }; + + class OptionGroupOptions : public Options + { + public: + + OptionGroupOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_option_defs (), + m_option_infos (), + m_did_finalize (false) + { + } + + virtual + ~OptionGroupOptions () + { + } + + + //---------------------------------------------------------------------- + /// Append options from a OptionGroup class. + /// + /// Append all options from \a group using the exact same option groups + /// that each option is defined with. + /// + /// @param[in] group + /// A group of options to take option values from and copy their + /// definitions into this class. + //---------------------------------------------------------------------- + void + Append (OptionGroup* group); + + //---------------------------------------------------------------------- + /// Append options from a OptionGroup class. + /// + /// Append options from \a group that have a usage mask that has any bits + /// in "src_mask" set. After the option definition is copied into the + /// options definitions in this class, set the usage_mask to "dst_mask". + /// + /// @param[in] group + /// A group of options to take option values from and copy their + /// definitions into this class. + /// + /// @param[in] src_mask + /// When copying options from \a group, you might only want some of + /// the options to be appended to this group. This mask allows you + /// to control which options from \a group get added. It also allows + /// you to specify the same options from \a group multiple times + /// for different option sets. + /// + /// @param[in] dst_mask + /// Set the usage mask for any copied options to \a dst_mask after + /// copying the option definition. + //---------------------------------------------------------------------- + void + Append (OptionGroup* group, + uint32_t src_mask, + uint32_t dst_mask); + + void + Finalize (); + + virtual Error + SetOptionValue (uint32_t option_idx, + const char *option_arg); + + virtual void + OptionParsingStarting (); + + virtual Error + OptionParsingFinished (); + + const OptionDefinition* + GetDefinitions () + { + assert (m_did_finalize); + return &m_option_defs[0]; + } + struct OptionInfo + { + OptionInfo (OptionGroup* g, uint32_t i) : + option_group (g), + option_index (i) + { + } + OptionGroup* option_group; // The group that this option came from + uint32_t option_index; // The original option index from the OptionGroup + }; + typedef std::vector OptionInfos; + + std::vector m_option_defs; + OptionInfos m_option_infos; + bool m_did_finalize; + }; + + +} // namespace lldb_private + +#endif // liblldb_Options_h_ diff --git a/include/lldb/Interpreter/Property.h b/include/lldb/Interpreter/Property.h new file mode 100644 index 00000000000..b192758cbc4 --- /dev/null +++ b/include/lldb/Interpreter/Property.h @@ -0,0 +1,109 @@ +//===-- Property.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Property_h_ +#define liblldb_Property_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Flags.h" +#include "lldb/Interpreter/OptionValue.h" + +namespace lldb_private { + + // A structure that can be used to create a global table for all properties. + // Property class instances can be constructed using one of these. + struct PropertyDefinition + { + const char *name; + OptionValue::Type type; + bool global; + uintptr_t default_uint_value; + const char *default_cstr_value; + OptionEnumValueElement *enum_values; + const char *description; + }; + + class Property + { + public: + Property (const PropertyDefinition &definition); + + Property (const ConstString &name, + const ConstString &desc, + bool is_global, + const lldb::OptionValueSP &value_sp); + + const ConstString & + GetName() const + { + return m_name; + } + + const char * + GetDescription () const + { + return m_description.GetCString(); + } + + const lldb::OptionValueSP & + GetValue() const + { + return m_value_sp; + } + + void + SetOptionValue (const lldb::OptionValueSP &value_sp) + { + m_value_sp = value_sp; + } + + + bool + IsValid() const + { + return (bool)m_value_sp; + } + + bool + IsGlobal () const + { + return m_is_global; + } + + void + Dump (const ExecutionContext *exe_ctx, + Stream &strm, + uint32_t dump_mask) const; + + bool + DumpQualifiedName(Stream &strm) const; + + void + DumpDescription (CommandInterpreter &interpreter, + Stream &strm, + uint32_t output_width, + bool display_qualified_name) const; + + protected: + ConstString m_name; + ConstString m_description; + lldb::OptionValueSP m_value_sp; + bool m_is_global; + }; + +} // namespace lldb_private + +#endif // liblldb_Property_h_ diff --git a/include/lldb/Interpreter/PythonDataObjects.h b/include/lldb/Interpreter/PythonDataObjects.h new file mode 100644 index 00000000000..b2c9240db09 --- /dev/null +++ b/include/lldb/Interpreter/PythonDataObjects.h @@ -0,0 +1,233 @@ +//===-- PythonDataObjects.h----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PythonDataObjects_h_ +#define liblldb_PythonDataObjects_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-defines.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Flags.h" +#include "lldb/Interpreter/OptionValue.h" +#if defined (__APPLE__) +#include +#else +#include +#endif + +namespace lldb_private { + + class PythonObject + { + public: + PythonObject () : + m_py_obj(NULL) + { + } + + PythonObject (PyObject* py_obj) : + m_py_obj(NULL) + { + Reset (py_obj); + } + + PythonObject (const PythonObject &rhs) : + m_py_obj(NULL) + { + Reset (rhs.m_py_obj); + } + + PythonObject (const lldb::ScriptInterpreterObjectSP &script_object_sp); + + virtual + ~PythonObject () + { + Reset (NULL); + } + + const PythonObject & + operator = (const PythonObject &rhs) + { + if (this != &rhs) + Reset (rhs.m_py_obj); + return *this; + } + + bool + Reset (const PythonObject &object) + { + return Reset(object.GetPythonObject()); + } + + virtual bool + Reset (PyObject* py_obj = NULL) + { + if (py_obj != m_py_obj) + { + Py_XDECREF(m_py_obj); + m_py_obj = py_obj; + Py_XINCREF(m_py_obj); + } + return true; + } + + void + Dump () const + { + if (m_py_obj) + _PyObject_Dump (m_py_obj); + else + puts ("NULL"); + } + + void + Dump (Stream &strm) const; + + PyObject* + GetPythonObject () const + { + return m_py_obj; + } + + PythonString + Repr (); + + PythonString + Str (); + + operator bool () const + { + return m_py_obj != NULL; + } + + protected: + PyObject* m_py_obj; + }; + + class PythonString: public PythonObject + { + public: + + PythonString (); + PythonString (PyObject *o); + PythonString (const PythonObject &object); + PythonString (const lldb::ScriptInterpreterObjectSP &script_object_sp); + PythonString (const char* string); + virtual ~PythonString (); + + virtual bool + Reset (PyObject* py_obj = NULL); + + const char* + GetString() const; + + size_t + GetSize() const; + + void + SetString (const char* string); + }; + + class PythonInteger: public PythonObject + { + public: + + PythonInteger (); + PythonInteger (PyObject* py_obj); + PythonInteger (const PythonObject &object); + PythonInteger (const lldb::ScriptInterpreterObjectSP &script_object_sp); + PythonInteger (int64_t value); + virtual ~PythonInteger (); + + virtual bool + Reset (PyObject* py_obj = NULL); + + int64_t + GetInteger(); + + void + SetInteger (int64_t value); + }; + + class PythonList: public PythonObject + { + public: + + PythonList (); + PythonList (PyObject* py_obj); + PythonList (const PythonObject &object); + PythonList (const lldb::ScriptInterpreterObjectSP &script_object_sp); + PythonList (uint32_t count); + virtual ~PythonList (); + + virtual bool + Reset (PyObject* py_obj = NULL); + + uint32_t + GetSize(); + + PythonObject + GetItemAtIndex (uint32_t index); + + void + SetItemAtIndex (uint32_t index, const PythonObject &object); + + void + AppendItem (const PythonObject &object); + }; + + class PythonDictionary: public PythonObject + { + public: + + PythonDictionary (); + PythonDictionary (PyObject* object); + PythonDictionary (const PythonObject &object); + PythonDictionary (const lldb::ScriptInterpreterObjectSP &script_object_sp); + virtual ~PythonDictionary (); + + virtual bool + Reset (PyObject* object = NULL); + + uint32_t GetSize(); + + PythonObject + GetItemForKey (const PythonString &key) const; + + const char * + GetItemForKeyAsString (const PythonString &key, const char *fail_value = NULL) const; + + int64_t + GetItemForKeyAsInteger (const PythonString &key, int64_t fail_value = 0) const; + + PythonObject + GetItemForKey (const char *key) const; + + typedef bool (*DictionaryIteratorCallback)(PythonString* key, PythonDictionary* dict); + + PythonList + GetKeys () const; + + PythonString + GetKeyAtPosition (uint32_t pos) const; + + PythonObject + GetValueAtPosition (uint32_t pos) const; + + void + SetItemForKey (const PythonString &key, const PythonObject& value); + }; + +} // namespace lldb_private + +#endif // liblldb_PythonDataObjects_h_ diff --git a/include/lldb/Interpreter/ScriptInterpreter.h b/include/lldb/Interpreter/ScriptInterpreter.h new file mode 100644 index 00000000000..9a66c775d47 --- /dev/null +++ b/include/lldb/Interpreter/ScriptInterpreter.h @@ -0,0 +1,519 @@ +//===-- ScriptInterpreter.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ScriptInterpreter_h_ +#define liblldb_ScriptInterpreter_h_ + +#include "lldb/lldb-private.h" + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Error.h" + +#include "lldb/Utility/PseudoTerminal.h" + + +namespace lldb_private { + +class ScriptInterpreterObject +{ +public: + ScriptInterpreterObject() : + m_object(NULL) + {} + + ScriptInterpreterObject(void* obj) : + m_object(obj) + {} + + ScriptInterpreterObject(const ScriptInterpreterObject& rhs) + : m_object(rhs.m_object) + {} + + virtual void* + GetObject() + { + return m_object; + } + + operator bool () + { + return m_object != NULL; + } + + ScriptInterpreterObject& + operator = (const ScriptInterpreterObject& rhs) + { + if (this != &rhs) + m_object = rhs.m_object; + return *this; + } + + virtual + ~ScriptInterpreterObject() + {} + +protected: + void* m_object; +}; + +class ScriptInterpreterLocker +{ +public: + + ScriptInterpreterLocker () + { + } + + virtual ~ScriptInterpreterLocker () + { + } +private: + DISALLOW_COPY_AND_ASSIGN (ScriptInterpreterLocker); +}; + + +class ScriptInterpreter +{ +public: + + typedef void (*SWIGInitCallback) (void); + + typedef bool (*SWIGBreakpointCallbackFunction) (const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::BreakpointLocationSP &bp_loc_sp); + + typedef bool (*SWIGWatchpointCallbackFunction) (const char *python_function_name, + const char *session_dictionary_name, + const lldb::StackFrameSP& frame_sp, + const lldb::WatchpointSP &wp_sp); + + typedef bool (*SWIGPythonTypeScriptCallbackFunction) (const char *python_function_name, + void *session_dictionary, + const lldb::ValueObjectSP& valobj_sp, + void** pyfunct_wrapper, + std::string& retval); + + typedef void* (*SWIGPythonCreateSyntheticProvider) (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp); + + typedef void* (*SWIGPythonCreateOSPlugin) (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ProcessSP& process_sp); + + typedef uint32_t (*SWIGPythonCalculateNumChildren) (void *implementor); + typedef void* (*SWIGPythonGetChildAtIndex) (void *implementor, uint32_t idx); + typedef int (*SWIGPythonGetIndexOfChildWithName) (void *implementor, const char* child_name); + typedef void* (*SWIGPythonCastPyObjectToSBValue) (void* data); + typedef bool (*SWIGPythonUpdateSynthProviderInstance) (void* data); + typedef bool (*SWIGPythonMightHaveChildrenSynthProviderInstance) (void* data); + + + typedef bool (*SWIGPythonCallCommand) (const char *python_function_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger, + const char* args, + lldb_private::CommandReturnObject& cmd_retobj); + + typedef bool (*SWIGPythonCallModuleInit) (const char *python_module_name, + const char *session_dictionary_name, + lldb::DebuggerSP& debugger); + + typedef bool (*SWIGPythonScriptKeyword_Process) (const char* python_function_name, + const char* session_dictionary_name, + lldb::ProcessSP& process, + std::string& output); + typedef bool (*SWIGPythonScriptKeyword_Thread) (const char* python_function_name, + const char* session_dictionary_name, + lldb::ThreadSP& thread, + std::string& output); + + typedef bool (*SWIGPythonScriptKeyword_Target) (const char* python_function_name, + const char* session_dictionary_name, + lldb::TargetSP& target, + std::string& output); + + typedef bool (*SWIGPythonScriptKeyword_Frame) (const char* python_function_name, + const char* session_dictionary_name, + lldb::StackFrameSP& frame, + std::string& output); + + + + typedef enum + { + eScriptReturnTypeCharPtr, + eScriptReturnTypeBool, + eScriptReturnTypeShortInt, + eScriptReturnTypeShortIntUnsigned, + eScriptReturnTypeInt, + eScriptReturnTypeIntUnsigned, + eScriptReturnTypeLongInt, + eScriptReturnTypeLongIntUnsigned, + eScriptReturnTypeLongLong, + eScriptReturnTypeLongLongUnsigned, + eScriptReturnTypeFloat, + eScriptReturnTypeDouble, + eScriptReturnTypeChar, + eScriptReturnTypeCharStrOrNone + } ScriptReturnType; + + ScriptInterpreter (CommandInterpreter &interpreter, lldb::ScriptLanguage script_lang); + + virtual ~ScriptInterpreter (); + + struct ExecuteScriptOptions + { + public: + ExecuteScriptOptions () : + m_enable_io(true), + m_set_lldb_globals(true), + m_maskout_errors(true) + { + } + + bool + GetEnableIO () const + { + return m_enable_io; + } + + bool + GetSetLLDBGlobals () const + { + return m_set_lldb_globals; + } + + bool + GetMaskoutErrors () const + { + return m_maskout_errors; + } + + ExecuteScriptOptions& + SetEnableIO (bool enable) + { + m_enable_io = enable; + return *this; + } + + ExecuteScriptOptions& + SetSetLLDBGlobals (bool set) + { + m_set_lldb_globals = set; + return *this; + } + + ExecuteScriptOptions& + SetMaskoutErrors (bool maskout) + { + m_maskout_errors = maskout; + return *this; + } + + private: + bool m_enable_io; + bool m_set_lldb_globals; + bool m_maskout_errors; + }; + + virtual bool + ExecuteOneLine (const char *command, + CommandReturnObject *result, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) = 0; + + virtual void + ExecuteInterpreterLoop () = 0; + + virtual bool + ExecuteOneLineWithReturn (const char *in_string, + ScriptReturnType return_type, + void *ret_value, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) + { + return true; + } + + virtual bool + ExecuteMultipleLines (const char *in_string, + const ExecuteScriptOptions &options = ExecuteScriptOptions()) + { + return true; + } + + virtual bool + ExportFunctionDefinitionToInterpreter (StringList &function_def) + { + return false; + } + + virtual bool + GenerateBreakpointCommandCallbackData (StringList &input, std::string& output) + { + return false; + } + + virtual bool + GenerateWatchpointCommandCallbackData (StringList &input, std::string& output) + { + return false; + } + + virtual bool + GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual bool + GenerateTypeScriptFunction (StringList &input, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual bool + GenerateScriptAliasFunction (StringList &input, std::string& output) + { + return false; + } + + virtual bool + GenerateTypeSynthClass (StringList &input, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual bool + GenerateTypeSynthClass (const char* oneliner, std::string& output, void* name_token = NULL) + { + return false; + } + + virtual lldb::ScriptInterpreterObjectSP + CreateSyntheticScriptedProvider (const char *class_name, + lldb::ValueObjectSP valobj) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreatePluginObject (const char *class_name, + lldb::ProcessSP process_sp) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_ThreadsInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterContextData (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t thread_id) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid, + lldb::addr_t context) + { + return lldb::ScriptInterpreterObjectSP(); + } + + virtual bool + GenerateFunction(const char *signature, const StringList &input) + { + return false; + } + + virtual void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + virtual void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result); + + /// Set a one-liner as the callback for the breakpoint. + virtual void + SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner) + { + return; + } + + /// Set a one-liner as the callback for the watchpoint. + virtual void + SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner) + { + return; + } + + virtual bool + GetScriptedSummary (const char *function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval) + { + return false; + } + + virtual size_t + CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor) + { + return 0; + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& implementor, uint32_t idx) + { + return lldb::ValueObjectSP(); + } + + virtual int + GetIndexOfChildWithName (const lldb::ScriptInterpreterObjectSP& implementor, const char* child_name) + { + return UINT32_MAX; + } + + virtual bool + UpdateSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor) + { + return false; + } + + virtual bool + MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor) + { + return true; + } + + virtual bool + RunScriptBasedCommand (const char* impl_function, + const char* args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject& cmd_retobj, + Error& error) + { + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Process* process, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Thread* thread, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Target* target, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + StackFrame* frame, + std::string& output, + Error& error) + { + error.SetErrorString("unimplemented"); + return false; + } + + virtual bool + GetDocumentationForItem (const char* item, std::string& dest) + { + dest.clear(); + return false; + } + + virtual bool + CheckObjectExists (const char* name) + { + return false; + } + + virtual bool + LoadScriptingModule (const char* filename, + bool can_reload, + bool init_session, + lldb_private::Error& error) + { + error.SetErrorString("loading unimplemented"); + return false; + } + + virtual lldb::ScriptInterpreterObjectSP + MakeScriptObject (void* object) + { + return lldb::ScriptInterpreterObjectSP(new ScriptInterpreterObject(object)); + } + + virtual std::unique_ptr + AcquireInterpreterLock (); + + const char * + GetScriptInterpreterPtyName (); + + int + GetMasterFileDescriptor (); + + CommandInterpreter & + GetCommandInterpreter (); + + static std::string + LanguageToString (lldb::ScriptLanguage language); + + static void + InitializeInterpreter (SWIGInitCallback python_swig_init_callback); + + static void + TerminateInterpreter (); + + virtual void + ResetOutputFileHandle (FILE *new_fh) { } //By default, do nothing. + +protected: + CommandInterpreter &m_interpreter; + lldb::ScriptLanguage m_script_lang; +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ScriptInterpreter_h_ diff --git a/include/lldb/Interpreter/ScriptInterpreterNone.h b/include/lldb/Interpreter/ScriptInterpreterNone.h new file mode 100644 index 00000000000..6c82b60b0bc --- /dev/null +++ b/include/lldb/Interpreter/ScriptInterpreterNone.h @@ -0,0 +1,35 @@ +//===-- ScriptInterpreterNone.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ScriptInterpreterNone_h_ +#define liblldb_ScriptInterpreterNone_h_ + +#include "lldb/Interpreter/ScriptInterpreter.h" + +namespace lldb_private { + +class ScriptInterpreterNone : public ScriptInterpreter +{ +public: + + ScriptInterpreterNone (CommandInterpreter &interpreter); + + ~ScriptInterpreterNone (); + + bool + ExecuteOneLine (const char *command, CommandReturnObject *result, const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + void + ExecuteInterpreterLoop (); + +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_ScriptInterpreterNone_h_ diff --git a/include/lldb/Interpreter/ScriptInterpreterPython.h b/include/lldb/Interpreter/ScriptInterpreterPython.h new file mode 100644 index 00000000000..2616f575d20 --- /dev/null +++ b/include/lldb/Interpreter/ScriptInterpreterPython.h @@ -0,0 +1,407 @@ +//===-- ScriptInterpreterPython.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_ScriptInterpreterPython_h_ +#define liblldb_ScriptInterpreterPython_h_ + +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Host/Terminal.h" + +namespace lldb_private { + +class ScriptInterpreterPython : public ScriptInterpreter +{ +public: + + ScriptInterpreterPython (CommandInterpreter &interpreter); + + ~ScriptInterpreterPython (); + + bool + ExecuteOneLine (const char *command, + CommandReturnObject *result, + const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + void + ExecuteInterpreterLoop (); + + bool + ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ScriptReturnType return_type, + void *ret_value, + const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + bool + ExecuteMultipleLines (const char *in_string, + const ExecuteScriptOptions &options = ExecuteScriptOptions()); + + bool + ExportFunctionDefinitionToInterpreter (StringList &function_def); + + bool + GenerateTypeScriptFunction (StringList &input, std::string& output, void* name_token = NULL); + + bool + GenerateTypeSynthClass (StringList &input, std::string& output, void* name_token = NULL); + + bool + GenerateTypeSynthClass (const char* oneliner, std::string& output, void* name_token = NULL); + + // use this if the function code is just a one-liner script + bool + GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token = NULL); + + virtual bool + GenerateScriptAliasFunction (StringList &input, std::string& output); + + lldb::ScriptInterpreterObjectSP + CreateSyntheticScriptedProvider (const char *class_name, + lldb::ValueObjectSP valobj); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreatePluginObject (const char *class_name, + lldb::ProcessSP process_sp); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_ThreadsInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_RegisterContextData (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t thread_id); + + virtual lldb::ScriptInterpreterObjectSP + OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid, + lldb::addr_t context); + + virtual size_t + CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor); + + virtual lldb::ValueObjectSP + GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& implementor, uint32_t idx); + + virtual int + GetIndexOfChildWithName (const lldb::ScriptInterpreterObjectSP& implementor, const char* child_name); + + virtual bool + UpdateSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor); + + virtual bool + MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor); + + virtual bool + RunScriptBasedCommand(const char* impl_function, + const char* args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject& cmd_retobj, + Error& error); + + bool + GenerateFunction(const char *signature, const StringList &input); + + bool + GenerateBreakpointCommandCallbackData (StringList &input, std::string& output); + + bool + GenerateWatchpointCommandCallbackData (StringList &input, std::string& output); + + static size_t + GenerateBreakpointOptionsCommandCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static size_t + GenerateWatchpointOptionsCommandCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + 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); + + virtual bool + GetScriptedSummary (const char *function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval); + + virtual bool + GetDocumentationForItem (const char* item, std::string& dest); + + virtual bool + CheckObjectExists (const char* name) + { + if (!name || !name[0]) + return false; + std::string temp; + return GetDocumentationForItem (name,temp); + } + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Process* process, + std::string& output, + Error& error); + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Thread* thread, + std::string& output, + Error& error); + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + Target* target, + std::string& output, + Error& error); + + virtual bool + RunScriptFormatKeyword (const char* impl_function, + StackFrame* frame, + std::string& output, + Error& error); + + virtual bool + LoadScriptingModule (const char* filename, + bool can_reload, + bool init_session, + lldb_private::Error& error); + + virtual lldb::ScriptInterpreterObjectSP + MakeScriptObject (void* object); + + virtual std::unique_ptr + AcquireInterpreterLock (); + + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result); + + void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result); + + /// Set a Python one-liner as the callback for the breakpoint. + void + SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner); + + /// Set a one-liner as the callback for the watchpoint. + void + SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner); + + StringList + ReadCommandInputFromUser (FILE *in_file); + + virtual void + ResetOutputFileHandle (FILE *new_fh); + + static lldb::thread_result_t + RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton); + + static void + InitializePrivate (); + + static void + InitializeInterpreter (SWIGInitCallback python_swig_init_callback); + +protected: + + bool + EnterSession (bool init_lldb_globals); + + void + LeaveSession (); + + void + SaveTerminalState (int fd); + + void + RestoreTerminalState (); + +private: + + class SynchronicityHandler + { + private: + lldb::DebuggerSP m_debugger_sp; + ScriptedCommandSynchronicity m_synch_wanted; + bool m_old_asynch; + public: + SynchronicityHandler(lldb::DebuggerSP, + ScriptedCommandSynchronicity); + ~SynchronicityHandler(); + }; + + class ScriptInterpreterPythonObject : public ScriptInterpreterObject + { + public: + ScriptInterpreterPythonObject() : + ScriptInterpreterObject() + {} + + ScriptInterpreterPythonObject(void* obj) : + ScriptInterpreterObject(obj) + { + Py_XINCREF(m_object); + } + + operator bool () + { + return m_object && m_object != Py_None; + } + + + virtual + ~ScriptInterpreterPythonObject() + { + Py_XDECREF(m_object); + m_object = NULL; + } + private: + DISALLOW_COPY_AND_ASSIGN (ScriptInterpreterPythonObject); + }; + + class Locker : public ScriptInterpreterLocker + { + public: + + enum OnEntry + { + AcquireLock = 0x0001, + InitSession = 0x0002, + InitGlobals = 0x0004 + }; + + enum OnLeave + { + FreeLock = 0x0001, + FreeAcquiredLock = 0x0002, // do not free the lock if we already held it when calling constructor + TearDownSession = 0x0004 + }; + + Locker (ScriptInterpreterPython *py_interpreter = NULL, + uint16_t on_entry = AcquireLock | InitSession, + uint16_t on_leave = FreeLock | TearDownSession, + FILE* wait_msg_handle = NULL); + + ~Locker (); + + private: + + bool + DoAcquireLock (); + + bool + DoInitSession (bool init_lldb_globals); + + bool + DoFreeLock (); + + bool + DoTearDownSession (); + + static void + ReleasePythonLock (); + + bool m_teardown_session; + ScriptInterpreterPython *m_python_interpreter; + FILE* m_tmp_fh; + PyGILState_STATE m_GILState; + }; + + class PythonInputReaderManager + { + public: + PythonInputReaderManager (ScriptInterpreterPython *interpreter); + + operator bool() + { + return m_error; + } + + ~PythonInputReaderManager(); + + private: + + static size_t + InputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + static lldb::thread_result_t + RunPythonInputReader (lldb::thread_arg_t baton); + + ScriptInterpreterPython *m_interpreter; + lldb::DebuggerSP m_debugger_sp; + lldb::InputReaderSP m_reader_sp; + bool m_error; + }; + + static size_t + InputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + + lldb_utility::PseudoTerminal m_embedded_thread_pty; + lldb_utility::PseudoTerminal m_embedded_python_pty; + lldb::InputReaderSP m_embedded_thread_input_reader_sp; + lldb::InputReaderSP m_embedded_python_input_reader_sp; + FILE *m_dbg_stdout; + PyObject *m_new_sysout; + PyObject *m_old_sysout; + PyObject *m_old_syserr; + PyObject *m_run_one_line; + std::string m_dictionary_name; + TerminalState m_terminal_state; + bool m_session_is_active; + bool m_pty_slave_is_open; + bool m_valid_session; + PyThreadState *m_command_thread_state; +}; +} // namespace lldb_private + +#endif // #ifdef LLDB_DISABLE_PYTHON + +#endif // #ifndef liblldb_ScriptInterpreterPython_h_ diff --git a/include/lldb/Symbol/Block.h b/include/lldb/Symbol/Block.h new file mode 100644 index 00000000000..a2d703b9069 --- /dev/null +++ b/include/lldb/Symbol/Block.h @@ -0,0 +1,496 @@ +//===-- Block.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Block_h_ +#define liblldb_Block_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/UserID.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Symbol/SymbolContext.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Block Block.h "lldb/Symbol/Block.h" +/// @brief A class that describes a single lexical block. +/// +/// A Function object owns a BlockList object which owns one or more +/// Block objects. The BlockList object contains a section offset +/// address range, and Block objects contain one or more ranges +/// which are offsets into that range. Blocks are can have discontiguous +/// ranges within the BlockList adress range, and each block can +/// contain child blocks each with their own sets of ranges. +/// +/// Each block has a variable list that represents local, argument, and +/// static variables that are scoped to the block. +/// +/// Inlined functions are representated by attaching a +/// InlineFunctionInfo shared pointer object to a block. Inlined +/// functions are represented as named blocks. +//---------------------------------------------------------------------- +class Block : + public UserID, + public SymbolContextScope +{ +public: + typedef RangeArray RangeList; + typedef RangeList::Entry Range; + + //------------------------------------------------------------------ + /// Construct with a User ID \a uid, \a depth. + /// + /// Initialize this block with the specified UID \a uid. The + /// \a depth in the \a block_list is used to represent the parent, + /// sibling, and child block information and also allows for partial + /// parsing at the block level. + /// + /// @param[in] uid + /// The UID for a given block. This value is given by the + /// SymbolFile plug-in and can be any value that helps the + /// SymbolFile plug-in to match this block back to the debug + /// information data that it parses for further or more in + /// depth parsing. Common values would be the index into a + /// table, or an offset into the debug information. + /// + /// @param[in] depth + /// The integer depth of this block in the block list hierarchy. + /// + /// @param[in] block_list + /// The block list that this object belongs to. + /// + /// @see BlockList + //------------------------------------------------------------------ + Block (lldb::user_id_t uid); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + virtual ~Block (); + + //------------------------------------------------------------------ + /// Add a child to this object. + /// + /// @param[in] child_block_sp + /// A shared pointer to a child block that will get added to + /// this block. + //------------------------------------------------------------------ + void + AddChild (const lldb::BlockSP &child_block_sp); + + //------------------------------------------------------------------ + /// Add a new offset range to this block. + /// + /// @param[in] start_offset + /// An offset into this Function's address range that + /// describes the start address of a range for this block. + /// + /// @param[in] end_offset + /// An offset into this Function's address range that + /// describes the end address of a range for this block. + //------------------------------------------------------------------ + void + AddRange (const Range& range); + + void + FinalizeRanges (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual CompileUnit * + CalculateSymbolContextCompileUnit (); + + virtual Function * + CalculateSymbolContextFunction (); + + virtual Block * + CalculateSymbolContextBlock (); + + //------------------------------------------------------------------ + /// Check if an offset is in one of the block offset ranges. + /// + /// @param[in] range_offset + /// An offset into the Function's address range. + /// + /// @return + /// Returns \b true if \a range_offset falls in one of this + /// block's ranges, \b false otherwise. + //------------------------------------------------------------------ + bool + Contains (lldb::addr_t range_offset) const; + + //------------------------------------------------------------------ + /// Check if a offset range is in one of the block offset ranges. + /// + /// @param[in] range + /// An offset range into the Function's address range. + /// + /// @return + /// Returns \b true if \a range falls in one of this + /// block's ranges, \b false otherwise. + //------------------------------------------------------------------ + bool + Contains (const Range& range) const; + + //------------------------------------------------------------------ + /// Check if this object contains "block" as a child block at any + /// depth. + /// + /// @param[in] block + /// A potential child block. + /// + /// @return + /// Returns \b true if \a block is a child of this block, \b + /// false otherwise. + //------------------------------------------------------------------ + bool + Contains (const Block *block) const; + + //------------------------------------------------------------------ + /// Dump the block contents. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] base_addr + /// The resolved start address of the Function's address + /// range. This should be resolved as the file or load address + /// prior to passing the value into this function for dumping. + /// + /// @param[in] depth + /// Limit the number of levels deep that this function should + /// print as this block can contain child blocks. Specify + /// INT_MAX to dump all child blocks. + /// + /// @param[in] show_context + /// If \b true, variables will dump their context information. + //------------------------------------------------------------------ + void + Dump (Stream *s, lldb::addr_t base_addr, int32_t depth, bool show_context) const; + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + void + DumpAddressRanges (Stream *s, + lldb::addr_t base_addr); + + void + GetDescription (Stream *s, + Function *function, + lldb::DescriptionLevel level, + Target *target) const; + + //------------------------------------------------------------------ + /// Get the parent block. + /// + /// @return + /// The parent block pointer, or NULL if this block has no + /// parent. + //------------------------------------------------------------------ + Block * + GetParent () const; + + + //------------------------------------------------------------------ + /// Get the inlined block that contains this block. + /// + /// @return + /// If this block contains inlined function info, it will return + /// this block, else parent blocks will be searched to see if + /// any contain this block. NULL will be returned if this block + /// nor any parent blocks are inlined function blocks. + //------------------------------------------------------------------ + Block * + GetContainingInlinedBlock (); + + //------------------------------------------------------------------ + /// Get the inlined parent block for this block. + /// + /// @return + /// The parent block pointer, or NULL if this block has no + /// parent. + //------------------------------------------------------------------ + Block * + GetInlinedParent (); + + //------------------------------------------------------------------ + /// Get the sibling block for this block. + /// + /// @return + /// The sibling block pointer, or NULL if this block has no + /// sibling. + //------------------------------------------------------------------ + Block * + GetSibling () const; + + //------------------------------------------------------------------ + /// Get the first child block. + /// + /// @return + /// The first child block pointer, or NULL if this block has no + /// children. + //------------------------------------------------------------------ + Block * + GetFirstChild () const + { + if (m_children.empty()) + return NULL; + return m_children.front().get(); + } + + //------------------------------------------------------------------ + /// Get the variable list for this block only. + /// + /// @param[in] can_create + /// If \b true, the variables can be parsed if they already + /// haven't been, else the current state of the block will be + /// returned. + /// + /// @return + /// A variable list shared pointer that contains all variables + /// for this block. + //------------------------------------------------------------------ + lldb::VariableListSP + GetBlockVariableList (bool can_create); + + + //------------------------------------------------------------------ + /// Get the variable list for this block and optionally all child + /// blocks if \a get_child_variables is \b true. + /// + /// @param[in] get_child_variables + /// If \b true, all variables from all child blocks will be + /// added to the variable list. + /// + /// @param[in] can_create + /// If \b true, the variables can be parsed if they already + /// haven't been, else the current state of the block will be + /// returned. Passing \b true for this parameter can be used + /// to see the current state of what has been parsed up to this + /// point. + /// + /// @param[in] add_inline_child_block_variables + /// If this is \b false, no child variables of child blocks + /// that are inlined functions will be gotten. If \b true then + /// all child variables will be added regardless of whether they + /// come from inlined functions or not. + /// + /// @return + /// A variable list shared pointer that contains all variables + /// for this block. + //------------------------------------------------------------------ + uint32_t + AppendBlockVariables (bool can_create, + bool get_child_block_variables, + bool stop_if_child_block_is_inlined_function, + VariableList *variable_list); + + //------------------------------------------------------------------ + /// Appends the variables from this block, and optionally from all + /// parent blocks, to \a variable_list. + /// + /// @param[in] can_create + /// If \b true, the variables can be parsed if they already + /// haven't been, else the current state of the block will be + /// returned. Passing \b true for this parameter can be used + /// to see the current state of what has been parsed up to this + /// point. + /// + /// @param[in] get_parent_variables + /// If \b true, all variables from all parent blocks will be + /// added to the variable list. + /// + /// @param[in] stop_if_block_is_inlined_function + /// If \b true, all variables from all parent blocks will be + /// added to the variable list until there are no parent blocks + /// or the parent block has inlined function info. + /// + /// @param[in/out] variable_list + /// All variables in this block, and optionally all parent + /// blocks will be added to this list. + /// + /// @return + /// The number of variable that were appended to \a + /// variable_list. + //------------------------------------------------------------------ + uint32_t + AppendVariables (bool can_create, + bool get_parent_variables, + bool stop_if_block_is_inlined_function, + VariableList *variable_list); + + //------------------------------------------------------------------ + /// Get const accessor for any inlined function information. + /// + /// @return + /// A comst pointer to any inlined function information, or NULL + /// if this is a regular block. + //------------------------------------------------------------------ + const InlineFunctionInfo* + GetInlinedFunctionInfo () const + { + return m_inlineInfoSP.get(); + } + + clang::DeclContext * + GetClangDeclContext(); + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// Returns the cost of this object plus any owned objects from the + /// ranges, variables, and inline function information. + /// + /// @return + /// The number of bytes that this object occupies in memory. + //------------------------------------------------------------------ + size_t + MemorySize() const; + + //------------------------------------------------------------------ + /// Set accessor for any inlined function information. + /// + /// @param[in] name + /// The method name for the inlined function. This value should + /// not be NULL. + /// + /// @param[in] mangled + /// The mangled method name for the inlined function. This can + /// be NULL if there is no mangled name for an inlined function + /// or if the name is the same as \a name. + /// + /// @param[in] decl_ptr + /// A optional pointer to declaration information for the + /// inlined function information. This value can be NULL to + /// indicate that no declaration information is available. + /// + /// @param[in] call_decl_ptr + /// Optional calling location declaration information that + /// describes from where this inlined function was called. + //------------------------------------------------------------------ + void + SetInlinedFunctionInfo (const char *name, + const char *mangled, + const Declaration *decl_ptr, + const Declaration *call_decl_ptr); + + + void + SetParentScope (SymbolContextScope *parent_scope) + { + m_parent_scope = parent_scope; + } + + //------------------------------------------------------------------ + /// Set accessor for the variable list. + /// + /// Called by the SymbolFile plug-ins after they have parsed the + /// variable lists and are ready to hand ownership of the list over + /// to this object. + /// + /// @param[in] variable_list_sp + /// A shared pointer to a VariableList. + //------------------------------------------------------------------ + void + SetVariableList (lldb::VariableListSP& variable_list_sp) + { + m_variable_list_sp = variable_list_sp; + } + + + + bool + BlockInfoHasBeenParsed() const + { + return m_parsed_block_info; + } + + void + SetBlockInfoHasBeenParsed (bool b, bool set_children); + + Block * + FindBlockByID (lldb::user_id_t block_id); + + size_t + GetNumRanges () const + { + return m_ranges.GetSize(); + } + + bool + GetRangeContainingOffset (const lldb::addr_t offset, Range &range); + + bool + GetRangeContainingAddress (const Address& addr, AddressRange &range); + + bool + GetRangeContainingLoadAddress (lldb::addr_t load_addr, Target &target, AddressRange &range); + + uint32_t + GetRangeIndexContainingAddress (const Address& addr); + + //------------------------------------------------------------------ + // Since blocks might have multiple discontiguous addresss ranges, + // we need to be able to get at any of the address ranges in a block. + //------------------------------------------------------------------ + bool + GetRangeAtIndex (uint32_t range_idx, + AddressRange &range); + + bool + GetStartAddress (Address &addr); + + void + SetDidParseVariables (bool b, bool set_children); + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + SymbolContextScope *m_parent_scope; + collection m_children; + RangeList m_ranges; + lldb::InlineFunctionInfoSP m_inlineInfoSP; ///< Inlined function information. + lldb::VariableListSP m_variable_list_sp; ///< The variable list for all local, static and paramter variables scoped to this block. + bool m_parsed_block_info:1, ///< Set to true if this block and it's children have all been parsed + m_parsed_block_variables:1, + m_parsed_child_blocks:1; + + // A parent of child blocks can be asked to find a sibling block given + // one of its child blocks + Block * + GetSiblingForChild (const Block *child_block) const; + +private: + DISALLOW_COPY_AND_ASSIGN (Block); +}; + + +} // namespace lldb_private + +#endif // liblldb_Block_h_ diff --git a/include/lldb/Symbol/ClangASTContext.h b/include/lldb/Symbol/ClangASTContext.h new file mode 100644 index 00000000000..75fc07b480e --- /dev/null +++ b/include/lldb/Symbol/ClangASTContext.h @@ -0,0 +1,441 @@ +//===-- ClangASTContext.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTContext_h_ +#define liblldb_ClangASTContext_h_ + +// C Includes +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/SmallVector.h" +#include "clang/AST/TemplateBase.h" + + +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private { + +class Declaration; + +class ClangASTContext +{ +public: + typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *); + typedef void (*CompleteObjCInterfaceDeclCallback)(void *baton, clang::ObjCInterfaceDecl *); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ClangASTContext (const char *triple = NULL); + + ~ClangASTContext(); + + clang::ASTContext * + getASTContext(); + + clang::Builtin::Context * + getBuiltinContext(); + + clang::IdentifierTable * + getIdentifierTable(); + + clang::LangOptions * + getLanguageOptions(); + + clang::SelectorTable * + getSelectorTable(); + + clang::FileManager * + getFileManager(); + + clang::SourceManager * + getSourceManager(); + + clang::DiagnosticsEngine * + getDiagnosticsEngine(); + + clang::DiagnosticConsumer * + getDiagnosticConsumer(); + + clang::TargetOptions * + getTargetOptions(); + + clang::TargetInfo * + getTargetInfo(); + + void + Clear(); + + const char * + GetTargetTriple (); + + void + SetTargetTriple (const char *target_triple); + + void + SetArchitecture (const ArchSpec &arch); + + bool + HasExternalSource (); + + void + SetExternalSource (llvm::OwningPtr &ast_source_ap); + + void + RemoveExternalSource (); + + bool + GetCompleteDecl (clang::Decl *decl) + { + return ClangASTContext::GetCompleteDecl(getASTContext(), decl); + } + + static bool + GetCompleteDecl (clang::ASTContext *ast, + clang::Decl *decl); + + void SetMetadataAsUserID (const void *object, + lldb::user_id_t user_id); + + void SetMetadata (const void *object, + ClangASTMetadata &meta_data) + { + SetMetadata(getASTContext(), object, meta_data); + } + + static void + SetMetadata (clang::ASTContext *ast, + const void *object, + ClangASTMetadata &meta_data); + + ClangASTMetadata * + GetMetadata (const void *object) + { + return GetMetadata(getASTContext(), object); + } + + static ClangASTMetadata * + GetMetadata (clang::ASTContext *ast, + const void *object); + + //------------------------------------------------------------------ + // Basic Types + //------------------------------------------------------------------ + ClangASTType + GetBuiltinTypeForEncodingAndBitSize (lldb::Encoding encoding, + uint32_t bit_size); + + static ClangASTType + GetBuiltinTypeForEncodingAndBitSize (clang::ASTContext *ast, + lldb::Encoding encoding, + uint32_t bit_size); + + ClangASTType + GetBasicType (lldb::BasicType type); + + static ClangASTType + GetBasicType (clang::ASTContext *ast, lldb::BasicType type); + + static ClangASTType + GetBasicType (clang::ASTContext *ast, const ConstString &name); + + static lldb::BasicType + GetBasicTypeEnumeration (const ConstString &name); + + ClangASTType + GetBuiltinTypeForDWARFEncodingAndBitSize ( + const char *type_name, + uint32_t dw_ate, + uint32_t bit_size); + + ClangASTType + GetCStringType(bool is_const); + + static ClangASTType + GetUnknownAnyType(clang::ASTContext *ast); + + ClangASTType + GetUnknownAnyType() + { + return ClangASTContext::GetUnknownAnyType(getASTContext()); + } + + uint32_t + GetPointerByteSize (); + + static clang::DeclContext * + GetTranslationUnitDecl (clang::ASTContext *ast); + + clang::DeclContext * + GetTranslationUnitDecl () + { + return GetTranslationUnitDecl (getASTContext()); + } + + static bool + GetClassMethodInfoForDeclContext (clang::DeclContext *decl_ctx, + lldb::LanguageType &language, + bool &is_instance_method, + ConstString &language_object_name); + + static ClangASTType + CopyType(clang::ASTContext *dest_context, + ClangASTType source_type); + + static clang::Decl * + CopyDecl (clang::ASTContext *dest_context, + clang::ASTContext *source_context, + clang::Decl *source_decl); + + static bool + AreTypesSame(ClangASTType type1, + ClangASTType type2, + bool ignore_qualifiers = false); + + ClangASTType + GetTypeForDecl (clang::TagDecl *decl); + + ClangASTType + GetTypeForDecl (clang::ObjCInterfaceDecl *objc_decl); + + //------------------------------------------------------------------ + // Structure, Unions, Classes + //------------------------------------------------------------------ + + static clang::AccessSpecifier + ConvertAccessTypeToAccessSpecifier (lldb::AccessType access); + + static clang::AccessSpecifier + UnifyAccessSpecifiers (clang::AccessSpecifier lhs, clang::AccessSpecifier rhs); + + static uint32_t + GetNumBaseClasses (const clang::CXXRecordDecl *cxx_record_decl, + bool omit_empty_base_classes); + + static uint32_t + GetIndexForRecordBase (const clang::RecordDecl *record_decl, + const clang::CXXBaseSpecifier *base_spec, + bool omit_empty_base_classes); + + ClangASTType + CreateRecordType (clang::DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *name, + int kind, + lldb::LanguageType language, + ClangASTMetadata *metadata = NULL); + + class TemplateParameterInfos + { + public: + bool + IsValid() const + { + if (args.empty()) + return false; + return args.size() == names.size(); + } + + size_t + GetSize () const + { + if (IsValid()) + return args.size(); + return 0; + } + + llvm::SmallVector names; + llvm::SmallVector args; + }; + + clang::FunctionTemplateDecl * + CreateFunctionTemplateDecl (clang::DeclContext *decl_ctx, + clang::FunctionDecl *func_decl, + const char *name, + const TemplateParameterInfos &infos); + + void + CreateFunctionTemplateSpecializationInfo (clang::FunctionDecl *func_decl, + clang::FunctionTemplateDecl *Template, + const TemplateParameterInfos &infos); + + clang::ClassTemplateDecl * + CreateClassTemplateDecl (clang::DeclContext *decl_ctx, + lldb::AccessType access_type, + const char *class_name, + int kind, + const TemplateParameterInfos &infos); + + clang::ClassTemplateSpecializationDecl * + CreateClassTemplateSpecializationDecl (clang::DeclContext *decl_ctx, + clang::ClassTemplateDecl *class_template_decl, + int kind, + const TemplateParameterInfos &infos); + + ClangASTType + CreateClassTemplateSpecializationType (clang::ClassTemplateSpecializationDecl *class_template_specialization_decl); + + static clang::DeclContext * + GetAsDeclContext (clang::CXXMethodDecl *cxx_method_decl); + + static clang::DeclContext * + GetAsDeclContext (clang::ObjCMethodDecl *objc_method_decl); + + + static bool + CheckOverloadedOperatorKindParameterCount (uint32_t op_kind, + uint32_t num_params); + + bool + FieldIsBitfield (clang::FieldDecl* field, + uint32_t& bitfield_bit_size); + + static bool + FieldIsBitfield (clang::ASTContext *ast, + clang::FieldDecl* field, + uint32_t& bitfield_bit_size); + + static bool + RecordHasFields (const clang::RecordDecl *record_decl); + + + ClangASTType + CreateObjCClass (const char *name, + clang::DeclContext *decl_ctx, + bool isForwardDecl, + bool isInternal, + ClangASTMetadata *metadata = NULL); + + // Returns a mask containing bits from the ClangASTContext::eTypeXXX enumerations + + + //------------------------------------------------------------------ + // Namespace Declarations + //------------------------------------------------------------------ + + clang::NamespaceDecl * + GetUniqueNamespaceDeclaration (const char *name, + clang::DeclContext *decl_ctx); + + //------------------------------------------------------------------ + // Function Types + //------------------------------------------------------------------ + + clang::FunctionDecl * + CreateFunctionDeclaration (clang::DeclContext *decl_ctx, + const char *name, + const ClangASTType &function_Type, + int storage, + bool is_inline); + + static ClangASTType + CreateFunctionType (clang::ASTContext *ast, + const ClangASTType &result_type, + const ClangASTType *args, + unsigned num_args, + bool is_variadic, + unsigned type_quals); + + ClangASTType + CreateFunctionType (const ClangASTType &result_type, + const ClangASTType *args, + unsigned num_args, + bool is_variadic, + unsigned type_quals) + { + return ClangASTContext::CreateFunctionType(getASTContext(), + result_type, + args, + num_args, + is_variadic, + type_quals); + } + + clang::ParmVarDecl * + CreateParameterDeclaration (const char *name, + const ClangASTType ¶m_type, + int storage); + + void + SetFunctionParameters (clang::FunctionDecl *function_decl, + clang::ParmVarDecl **params, + unsigned num_params); + + //------------------------------------------------------------------ + // Array Types + //------------------------------------------------------------------ + + ClangASTType + CreateArrayType (const ClangASTType &element_type, + size_t element_count, + bool is_vector); + + //------------------------------------------------------------------ + // Enumeration Types + //------------------------------------------------------------------ + ClangASTType + CreateEnumerationType (const char *name, + clang::DeclContext *decl_ctx, + const Declaration &decl, + const ClangASTType &integer_qual_type); + + //------------------------------------------------------------------ + // Floating point functions + //------------------------------------------------------------------ + + ClangASTType + GetFloatTypeFromBitSize (size_t bit_size) + { + return GetFloatTypeFromBitSize (getASTContext(), bit_size); + } + + static ClangASTType + GetFloatTypeFromBitSize (clang::ASTContext *ast, + size_t bit_size); +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangASTContext can see and modify these + //------------------------------------------------------------------ + std::string m_target_triple; + std::unique_ptr m_ast_ap; + std::unique_ptr m_language_options_ap; + std::unique_ptr m_file_manager_ap; + std::unique_ptr m_file_system_options_ap; + std::unique_ptr m_source_manager_ap; + std::unique_ptr m_diagnostics_engine_ap; + std::unique_ptr m_diagnostic_consumer_ap; + llvm::IntrusiveRefCntPtr m_target_options_rp; + std::unique_ptr m_target_info_ap; + std::unique_ptr m_identifier_table_ap; + std::unique_ptr m_selector_table_ap; + std::unique_ptr m_builtins_ap; + CompleteTagDeclCallback m_callback_tag_decl; + CompleteObjCInterfaceDeclCallback m_callback_objc_decl; + void * m_callback_baton; + uint32_t m_pointer_byte_size; +private: + //------------------------------------------------------------------ + // For ClangASTContext only + //------------------------------------------------------------------ + ClangASTContext(const ClangASTContext&); + const ClangASTContext& operator=(const ClangASTContext&); +}; + +} // namespace lldb_private + +#endif // liblldb_ClangASTContext_h_ diff --git a/include/lldb/Symbol/ClangASTImporter.h b/include/lldb/Symbol/ClangASTImporter.h new file mode 100644 index 00000000000..10df7da893a --- /dev/null +++ b/include/lldb/Symbol/ClangASTImporter.h @@ -0,0 +1,371 @@ +//===-- ClangASTImporter.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTImporter_h_ +#define liblldb_ClangASTImporter_h_ + +#include +#include + +#include "lldb/lldb-types.h" +#include "clang/AST/ASTImporter.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/FileSystemOptions.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" + +namespace lldb_private { + +class ClangASTMetrics +{ +public: + static void DumpCounters (Log *log); + static void ClearLocalCounters () + { + local_counters = { 0, 0, 0, 0, 0, 0 }; + } + + static void RegisterVisibleQuery () + { + ++global_counters.m_visible_query_count; + ++local_counters.m_visible_query_count; + } + + static void RegisterLexicalQuery () + { + ++global_counters.m_lexical_query_count; + ++local_counters.m_lexical_query_count; + } + + static void RegisterLLDBImport () + { + ++global_counters.m_lldb_import_count; + ++local_counters.m_lldb_import_count; + } + + static void RegisterClangImport () + { + ++global_counters.m_clang_import_count; + ++local_counters.m_clang_import_count; + } + + static void RegisterDeclCompletion () + { + ++global_counters.m_decls_completed_count; + ++local_counters.m_decls_completed_count; + } + + static void RegisterRecordLayout () + { + ++global_counters.m_record_layout_count; + ++local_counters.m_record_layout_count; + } + +private: + struct Counters + { + uint64_t m_visible_query_count; + uint64_t m_lexical_query_count; + uint64_t m_lldb_import_count; + uint64_t m_clang_import_count; + uint64_t m_decls_completed_count; + uint64_t m_record_layout_count; + }; + + static Counters global_counters; + static Counters local_counters; + + static void DumpCounters (Log *log, Counters &counters); +}; + +class ClangASTImporter +{ +public: + ClangASTImporter () : + m_file_manager(clang::FileSystemOptions()) + { + } + + clang::QualType + CopyType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::QualType type); + + lldb::clang_type_t + CopyType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + lldb::clang_type_t type); + + clang::Decl * + CopyDecl (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::Decl *decl); + + lldb::clang_type_t + DeportType (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + lldb::clang_type_t type); + + clang::Decl * + DeportDecl (clang::ASTContext *dst_ctx, + clang::ASTContext *src_ctx, + clang::Decl *decl); + + void + CompleteDecl (clang::Decl *decl); + + bool + CompleteTagDecl (clang::TagDecl *decl); + + bool + CompleteTagDeclWithOrigin (clang::TagDecl *decl, clang::TagDecl *origin); + + bool + CompleteObjCInterfaceDecl (clang::ObjCInterfaceDecl *interface_decl); + + bool + RequireCompleteType (clang::QualType type); + + bool + ResolveDeclOrigin (const clang::Decl *decl, clang::Decl **original_decl, clang::ASTContext **original_ctx) + { + DeclOrigin origin = GetDeclOrigin(decl); + + if (original_decl) + *original_decl = origin.decl; + + if (original_ctx) + *original_ctx = origin.ctx; + + return origin.Valid(); + } + + void + SetDeclOrigin (const clang::Decl *decl, clang::Decl *original_decl); + + ClangASTMetadata * + GetDeclMetadata (const clang::Decl *decl); + + // + // Namespace maps + // + + typedef std::vector < std::pair > NamespaceMap; + typedef std::shared_ptr NamespaceMapSP; + + void RegisterNamespaceMap (const clang::NamespaceDecl *decl, + NamespaceMapSP &namespace_map); + + NamespaceMapSP GetNamespaceMap (const clang::NamespaceDecl *decl); + + void BuildNamespaceMap (const clang::NamespaceDecl *decl); + + // + // Comleters for maps + // + + class MapCompleter + { + public: + virtual ~MapCompleter (); + + virtual void CompleteNamespaceMap (NamespaceMapSP &namespace_map, + const ConstString &name, + NamespaceMapSP &parent_map) const = 0; + }; + + void InstallMapCompleter (clang::ASTContext *dst_ctx, MapCompleter &completer) + { + ASTContextMetadataSP context_md; + ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx); + + if (context_md_iter == m_metadata_map.end()) + { + context_md = ASTContextMetadataSP(new ASTContextMetadata(dst_ctx)); + m_metadata_map[dst_ctx] = context_md; + } + else + { + context_md = context_md_iter->second; + } + + context_md->m_map_completer = &completer; + } + + void ForgetDestination (clang::ASTContext *dst_ctx); + void ForgetSource (clang::ASTContext *dst_ctx, clang::ASTContext *src_ctx); +private: + struct DeclOrigin + { + DeclOrigin () : + ctx(NULL), + decl(NULL) + { + } + + DeclOrigin (clang::ASTContext *_ctx, + clang::Decl *_decl) : + ctx(_ctx), + decl(_decl) + { + } + + DeclOrigin (const DeclOrigin &rhs) + { + ctx = rhs.ctx; + decl = rhs.decl; + } + + void operator= (const DeclOrigin &rhs) + { + ctx = rhs.ctx; + decl = rhs.decl; + } + + bool + Valid () + { + return (ctx != NULL || decl != NULL); + } + + clang::ASTContext *ctx; + clang::Decl *decl; + }; + + typedef std::map OriginMap; + + class Minion : public clang::ASTImporter + { + public: + Minion (ClangASTImporter &master, + clang::ASTContext *target_ctx, + clang::ASTContext *source_ctx) : + clang::ASTImporter(*target_ctx, + master.m_file_manager, + *source_ctx, + master.m_file_manager, + true /*minimal*/), + m_decls_to_deport(NULL), + m_decls_already_deported(NULL), + m_master(master), + m_source_ctx(source_ctx) + { + } + + // A call to "InitDeportWorkQueues" puts the minion into deport mode. + // In deport mode, every copied Decl that could require completion is + // recorded and placed into the decls_to_deport set. + // + // A call to "ExecuteDeportWorkQueues" completes all the Decls that + // are in decls_to_deport, adding any Decls it sees along the way that + // it hasn't already deported. It proceeds until decls_to_deport is + // empty. + // + // These calls must be paired. Leaving a minion in deport mode or + // trying to start deport minion with a new pair of queues will result + // in an assertion failure. + + void InitDeportWorkQueues (std::set *decls_to_deport, + std::set *decls_already_deported); + void ExecuteDeportWorkQueues (); + + void ImportDefinitionTo (clang::Decl *to, clang::Decl *from); + + clang::Decl *Imported (clang::Decl *from, clang::Decl *to); + + std::set *m_decls_to_deport; + std::set *m_decls_already_deported; + ClangASTImporter &m_master; + clang::ASTContext *m_source_ctx; + }; + + typedef std::shared_ptr MinionSP; + typedef std::map MinionMap; + typedef std::map NamespaceMetaMap; + + struct ASTContextMetadata + { + ASTContextMetadata(clang::ASTContext *dst_ctx) : + m_dst_ctx (dst_ctx), + m_minions (), + m_origins (), + m_namespace_maps (), + m_map_completer (NULL) + { + } + + clang::ASTContext *m_dst_ctx; + MinionMap m_minions; + OriginMap m_origins; + + NamespaceMetaMap m_namespace_maps; + MapCompleter *m_map_completer; + }; + + typedef std::shared_ptr ASTContextMetadataSP; + typedef std::map ContextMetadataMap; + + ContextMetadataMap m_metadata_map; + + ASTContextMetadataSP + GetContextMetadata (clang::ASTContext *dst_ctx) + { + ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx); + + if (context_md_iter == m_metadata_map.end()) + { + ASTContextMetadataSP context_md = ASTContextMetadataSP(new ASTContextMetadata(dst_ctx)); + m_metadata_map[dst_ctx] = context_md; + return context_md; + } + else + { + return context_md_iter->second; + } + } + + ASTContextMetadataSP + MaybeGetContextMetadata (clang::ASTContext *dst_ctx) + { + ContextMetadataMap::iterator context_md_iter = m_metadata_map.find(dst_ctx); + + if (context_md_iter != m_metadata_map.end()) + return context_md_iter->second; + else + return ASTContextMetadataSP(); + } + + MinionSP + GetMinion (clang::ASTContext *dst_ctx, clang::ASTContext *src_ctx) + { + ASTContextMetadataSP context_md = GetContextMetadata(dst_ctx); + + MinionMap &minions = context_md->m_minions; + MinionMap::iterator minion_iter = minions.find(src_ctx); + + if (minion_iter == minions.end()) + { + MinionSP minion = MinionSP(new Minion(*this, dst_ctx, src_ctx)); + minions[src_ctx] = minion; + return minion; + } + else + { + return minion_iter->second; + } + } + + DeclOrigin + GetDeclOrigin (const clang::Decl *decl); + + clang::FileManager m_file_manager; +}; + +} + +#endif diff --git a/include/lldb/Symbol/ClangASTType.h b/include/lldb/Symbol/ClangASTType.h new file mode 100644 index 00000000000..d9e754e8ceb --- /dev/null +++ b/include/lldb/Symbol/ClangASTType.h @@ -0,0 +1,679 @@ +//===-- ClangASTType.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangASTType_h_ +#define liblldb_ClangASTType_h_ + +#include +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "clang/AST/Type.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// A class that can carry around a clang ASTContext and a opaque clang +// QualType. A clang::QualType can be easily reconstructed from an +// opaque clang type and often the ASTContext is needed when doing +// various type related tasks, so this class allows both items to travel +// in a single very lightweight class that can be used. There are many +// static equivalents of the member functions that allow the ASTContext +// and the opaque clang QualType to be specified for ease of use and +// to avoid code duplication. +//---------------------------------------------------------------------- +class ClangASTType +{ +public: + enum { + eTypeHasChildren = (1u << 0), + eTypeHasValue = (1u << 1), + eTypeIsArray = (1u << 2), + eTypeIsBlock = (1u << 3), + eTypeIsBuiltIn = (1u << 4), + eTypeIsClass = (1u << 5), + eTypeIsCPlusPlus = (1u << 6), + eTypeIsEnumeration = (1u << 7), + eTypeIsFuncPrototype = (1u << 8), + eTypeIsMember = (1u << 9), + eTypeIsObjC = (1u << 10), + eTypeIsPointer = (1u << 11), + eTypeIsReference = (1u << 12), + eTypeIsStructUnion = (1u << 13), + eTypeIsTemplate = (1u << 14), + eTypeIsTypedef = (1u << 15), + eTypeIsVector = (1u << 16), + eTypeIsScalar = (1u << 17), + eTypeIsInteger = (1u << 18), + eTypeIsFloat = (1u << 19), + eTypeIsComplex = (1u << 20), + eTypeIsSigned = (1u << 21) + }; + + + //---------------------------------------------------------------------- + // Constructors and Destructors + //---------------------------------------------------------------------- + ClangASTType (clang::ASTContext *ast_context, lldb::clang_type_t type) : + m_type (type), + m_ast (ast_context) + { + } + + ClangASTType (clang::ASTContext *ast_context, clang::QualType qual_type); + + ClangASTType (const ClangASTType &rhs) : + m_type (rhs.m_type), + m_ast (rhs.m_ast) + { + } + + ClangASTType () : + m_type (0), + m_ast (0) + { + } + + ~ClangASTType(); + + //---------------------------------------------------------------------- + // Operators + //---------------------------------------------------------------------- + + const ClangASTType & + operator= (const ClangASTType &rhs) + { + m_type = rhs.m_type; + m_ast = rhs.m_ast; + return *this; + } + + + //---------------------------------------------------------------------- + // Tests + //---------------------------------------------------------------------- + + operator bool () const + { + return m_type != NULL && m_ast != NULL; + } + + bool + operator < (const ClangASTType &rhs) const + { + if (m_ast == rhs.m_ast) + return m_type < rhs.m_type; + return m_ast < rhs.m_ast; + } + + bool + IsValid () const + { + return m_type != NULL && m_ast != NULL; + } + + bool + IsArrayType (ClangASTType *element_type, + uint64_t *size, + bool *is_incomplete) const; + + bool + IsArrayOfScalarType () const; + + bool + IsAggregateType () const; + + bool + IsBeingDefined () const; + + bool + IsCharType () const; + + bool + IsCompleteType () const; + + bool + IsConst() const; + + bool + IsCStringType (uint32_t &length) const; + + bool + IsCXXClassType () const; + + bool + IsDefined() const; + + bool + IsFloatingPointType (uint32_t &count, bool &is_complex) const; + + bool + IsFunctionType (bool *is_variadic_ptr = NULL) const; + + bool + IsVariadicFunctionType () const; + + bool + IsFunctionPointerType () const; + + bool + IsIntegerType (bool &is_signed) const; + + bool + IsObjCClassType () const; + + bool + IsObjCClassTypeAndHasIVars (bool check_superclass) const; + + bool + IsObjCObjectOrInterfaceType () const; + + bool + IsObjCObjectPointerType (ClangASTType *target_type = NULL); + + bool + IsPolymorphicClass () const; + + bool + IsPossibleCPlusPlusDynamicType (ClangASTType *target_type = NULL) const + { + return IsPossibleDynamicType (target_type, true, false); + } + + bool + IsPossibleDynamicType (ClangASTType *target_type, // Can pass NULL + bool check_cplusplus, + bool check_objc) const; + + + bool + IsPointerToScalarType () const; + + bool + IsPointerType (ClangASTType *pointee_type = NULL) const; + + bool + IsPointerOrReferenceType (ClangASTType *pointee_type = NULL) const; + + bool + IsReferenceType (ClangASTType *pointee_type = NULL) const; + + bool + IsScalarType () const; + + bool + IsTypedefType () const; + + bool + IsVoidType () const; + + bool + GetCXXClassName (std::string &class_name) const; + + bool + GetObjCClassName (std::string &class_name); + + + //---------------------------------------------------------------------- + // Type Completion + //---------------------------------------------------------------------- + + bool + GetCompleteType () const; + + //---------------------------------------------------------------------- + // AST related queries + //---------------------------------------------------------------------- + + size_t + GetPointerByteSize () const; + + //---------------------------------------------------------------------- + // Accessors + //---------------------------------------------------------------------- + + clang::ASTContext * + GetASTContext() const + { + return m_ast; + } + + ConstString + GetConstQualifiedTypeName () const; + + ConstString + GetConstTypeName () const; + + std::string + GetTypeName () const; + + uint32_t + GetTypeInfo (ClangASTType *pointee_or_element_clang_type = NULL) const; + + lldb::LanguageType + GetMinimumLanguage (); + + lldb::clang_type_t + GetOpaqueQualType() const + { + return m_type; + } + + lldb::TypeClass + GetTypeClass () const; + + void + SetClangType (clang::ASTContext *ast, lldb::clang_type_t type) + { + m_ast = ast; + m_type = type; + } + + void + SetClangType (clang::ASTContext *ast, clang::QualType qual_type); + + unsigned + GetTypeQualifiers() const; + + //---------------------------------------------------------------------- + // Creating related types + //---------------------------------------------------------------------- + + ClangASTType + AddConstModifier () const; + + ClangASTType + AddRestrictModifier () const; + + ClangASTType + AddVolatileModifier () const; + + // Using the current type, create a new typedef to that type using "typedef_name" + // as the name and "decl_ctx" as the decl context. + ClangASTType + CreateTypedefType (const char *typedef_name, + clang::DeclContext *decl_ctx) const; + + ClangASTType + GetArrayElementType (uint64_t& stride) const; + + ClangASTType + GetCanonicalType () const; + + ClangASTType + GetFullyUnqualifiedType () const; + + // Returns -1 if this isn't a function of if the fucntion doesn't have a prototype + // Returns a value >= 0 if there is a prototype. + int + GetFunctionArgumentCount () const; + + ClangASTType + GetFunctionArgumentTypeAtIndex (size_t idx); + + ClangASTType + GetFunctionReturnType () const; + + ClangASTType + GetLValueReferenceType () const; + + ClangASTType + GetNonReferenceType () const; + + ClangASTType + GetPointeeType () const; + + ClangASTType + GetPointerType () const; + + ClangASTType + GetRValueReferenceType () const; + + // If the current object represents a typedef type, get the underlying type + ClangASTType + GetTypedefedType () const; + + ClangASTType + RemoveFastQualifiers () const; + + //---------------------------------------------------------------------- + // Create related types using the current type's AST + //---------------------------------------------------------------------- + ClangASTType + GetBasicTypeFromAST (lldb::BasicType basic_type) const; + + //---------------------------------------------------------------------- + // Exploring the type + //---------------------------------------------------------------------- + + uint64_t + GetByteSize () const; + + uint64_t + GetBitSize () const; + + lldb::Encoding + GetEncoding (uint64_t &count) const; + + lldb::Format + GetFormat () const; + + size_t + GetTypeBitAlign () const; + + uint32_t + GetNumChildren (bool omit_empty_base_classes) const; + + lldb::BasicType + GetBasicTypeEnumeration () const; + + static lldb::BasicType + GetBasicTypeEnumeration (const ConstString &name); + + uint32_t + GetNumDirectBaseClasses () const; + + uint32_t + GetNumVirtualBaseClasses () const; + + uint32_t + GetNumFields () const; + + ClangASTType + GetDirectBaseClassAtIndex (size_t idx, + uint32_t *bit_offset_ptr) const; + + ClangASTType + GetVirtualBaseClassAtIndex (size_t idx, + uint32_t *bit_offset_ptr) const; + + ClangASTType + GetFieldAtIndex (size_t idx, + std::string& name, + uint64_t *bit_offset_ptr, + uint32_t *bitfield_bit_size_ptr, + bool *is_bitfield_ptr) const; + + uint32_t + GetIndexOfFieldWithName (const char* name, + ClangASTType* field_clang_type = NULL, + uint64_t *bit_offset_ptr = NULL, + uint32_t *bitfield_bit_size_ptr = NULL, + bool *is_bitfield_ptr = NULL) const; + + uint32_t + GetNumPointeeChildren () const; + + ClangASTType + GetChildClangTypeAtIndex (ExecutionContext *exe_ctx, + const char *parent_name, + 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) const; + + // Lookup a child given a name. This function will match base class names + // and member member names in "clang_type" only, not descendants. + uint32_t + GetIndexOfChildWithName (const char *name, + bool omit_empty_base_classes) const; + + // Lookup a child member given a name. This function will match member names + // only and will descend into "clang_type" children in search for the first + // member in this class, or any base class that matches "name". + // TODO: Return all matches for a given name by returning a vector> + // so we catch all names that match a given child name, not just the first. + size_t + GetIndexOfChildMemberWithName (const char *name, + bool omit_empty_base_classes, + std::vector& child_indexes) const; + + size_t + GetNumTemplateArguments () const; + + ClangASTType + GetTemplateArgument (size_t idx, + lldb::TemplateArgumentKind &kind) const; + + + //---------------------------------------------------------------------- + // Modifying RecordType + //---------------------------------------------------------------------- + clang::FieldDecl * + AddFieldToRecordType (const char *name, + const ClangASTType &field_type, + lldb::AccessType access, + uint32_t bitfield_bit_size); + + void + BuildIndirectFields (); + + clang::VarDecl * + AddVariableToRecordType (const char *name, + const ClangASTType &var_type, + lldb::AccessType access); + + clang::CXXMethodDecl * + AddMethodToCXXRecordType (const char *name, + const ClangASTType &method_type, + lldb::AccessType access, + bool is_virtual, + bool is_static, + bool is_inline, + bool is_explicit, + bool is_attr_used, + bool is_artificial); + + // C++ Base Classes + clang::CXXBaseSpecifier * + CreateBaseClassSpecifier (lldb::AccessType access, + bool is_virtual, + bool base_of_class); + + static void + DeleteBaseClassSpecifiers (clang::CXXBaseSpecifier **base_classes, + unsigned num_base_classes); + + bool + SetBaseClassesForClassType (clang::CXXBaseSpecifier const * const *base_classes, + unsigned num_base_classes); + + + bool + SetObjCSuperClass (const ClangASTType &superclass_clang_type); + + bool + AddObjCClassProperty (const char *property_name, + const ClangASTType &property_clang_type, + clang::ObjCIvarDecl *ivar_decl, + const char *property_setter_name, + const char *property_getter_name, + uint32_t property_attributes, + ClangASTMetadata *metadata); + + clang::ObjCMethodDecl * + AddMethodToObjCObjectType (const char *name, // the full symbol name as seen in the symbol table ("-[NString stringWithCString:]") + const ClangASTType &method_clang_type, + lldb::AccessType access, + bool is_artificial); + + clang::DeclContext * + GetDeclContextForType () const; + + + bool + SetDefaultAccessForRecordFields (int default_accessibility, + int *assigned_accessibilities, + size_t num_assigned_accessibilities); + + bool + SetHasExternalStorage (bool has_extern); + + + //------------------------------------------------------------------ + // clang::TagType + //------------------------------------------------------------------ + + bool + SetTagTypeKind (int kind) const; + + //------------------------------------------------------------------ + // Tag Declarations + //------------------------------------------------------------------ + bool + StartTagDeclarationDefinition (); + + bool + CompleteTagDeclarationDefinition (); + + //---------------------------------------------------------------------- + // Modifying Enumeration types + //---------------------------------------------------------------------- + bool + AddEnumerationValueToEnumerationType (const ClangASTType &enumerator_qual_type, + const Declaration &decl, + const char *name, + int64_t enum_value, + uint32_t enum_value_bit_size); + + + + ClangASTType + GetEnumerationIntegerType () const; + + + //------------------------------------------------------------------ + // Pointers & References + //------------------------------------------------------------------ + + // Call this function using the class type when you want to make a + // member pointer type to pointee_type. + ClangASTType + CreateMemberPointerType (const ClangASTType &pointee_type) const; + + + // Converts "s" to a floating point value and place resulting floating + // point bytes in the "dst" buffer. + size_t + ConvertStringToFloatValue (const char *s, + uint8_t *dst, + size_t dst_size) const; + //---------------------------------------------------------------------- + // Dumping types + //---------------------------------------------------------------------- + void + DumpValue (ExecutionContext *exe_ctx, + Stream *s, + lldb::Format format, + const DataExtractor &data, + lldb::offset_t data_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); + + bool + DumpTypeValue (Stream *s, + lldb::Format format, + const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size, + uint32_t bitfield_bit_size, + uint32_t bitfield_bit_offset, + ExecutionContextScope *exe_scope); + + void + DumpSummary (ExecutionContext *exe_ctx, + Stream *s, + const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size); + + void + DumpTypeDescription () const; // Dump to stdout + + void + DumpTypeDescription (Stream *s) const; + + bool + GetValueAsScalar (const DataExtractor &data, + lldb::offset_t data_offset, + size_t data_byte_size, + Scalar &value) const; + + bool + SetValueFromScalar (const Scalar &value, + Stream &strm); + + bool + ReadFromMemory (ExecutionContext *exe_ctx, + lldb::addr_t addr, + AddressType address_type, + DataExtractor &data); + + bool + WriteToMemory (ExecutionContext *exe_ctx, + lldb::addr_t addr, + AddressType address_type, + StreamString &new_value); + + + clang::RecordDecl * + GetAsRecordDecl () const; + + clang::CXXRecordDecl * + GetAsCXXRecordDecl () const; + + clang::ObjCInterfaceDecl * + GetAsObjCInterfaceDecl () const; + + void + Clear() + { + m_type = NULL; + m_ast = NULL; + } + + clang::QualType + GetQualType () const + { + if (m_type) + return clang::QualType::getFromOpaquePtr(m_type); + return clang::QualType(); + } + clang::QualType + GetCanonicalQualType () const + { + if (m_type) + return clang::QualType::getFromOpaquePtr(m_type).getCanonicalType(); + return clang::QualType(); + } + +private: + lldb::clang_type_t m_type; + clang::ASTContext *m_ast; + +}; + +bool operator == (const ClangASTType &lhs, const ClangASTType &rhs); +bool operator != (const ClangASTType &lhs, const ClangASTType &rhs); + + +} // namespace lldb_private + +#endif // #ifndef liblldb_ClangASTType_h_ diff --git a/include/lldb/Symbol/ClangExternalASTSourceCallbacks.h b/include/lldb/Symbol/ClangExternalASTSourceCallbacks.h new file mode 100644 index 00000000000..0c8121135ef --- /dev/null +++ b/include/lldb/Symbol/ClangExternalASTSourceCallbacks.h @@ -0,0 +1,171 @@ +//===-- ClangExternalASTSourceCallbacks.h -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExternalASTSourceCallbacks_h_ +#define liblldb_ClangExternalASTSourceCallbacks_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +#include "clang/AST/CharUnits.h" + +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.h" + +namespace lldb_private { + +class ClangExternalASTSourceCallbacks : public ClangExternalASTSourceCommon +{ +public: + + typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *); + typedef void (*CompleteObjCInterfaceDeclCallback)(void *baton, clang::ObjCInterfaceDecl *); + typedef void (*FindExternalVisibleDeclsByNameCallback)(void *baton, const clang::DeclContext *DC, clang::DeclarationName Name, llvm::SmallVectorImpl *results); + typedef bool (*LayoutRecordTypeCallback)(void *baton, + const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets); + + ClangExternalASTSourceCallbacks (CompleteTagDeclCallback tag_decl_callback, + CompleteObjCInterfaceDeclCallback objc_decl_callback, + FindExternalVisibleDeclsByNameCallback find_by_name_callback, + LayoutRecordTypeCallback layout_record_type_callback, + void *callback_baton) : + m_callback_tag_decl (tag_decl_callback), + m_callback_objc_decl (objc_decl_callback), + m_callback_find_by_name (find_by_name_callback), + m_callback_layout_record_type (layout_record_type_callback), + m_callback_baton (callback_baton) + { + } + + //------------------------------------------------------------------ + // clang::ExternalASTSource + //------------------------------------------------------------------ + + virtual clang::Decl * + GetExternalDecl (uint32_t ID) + { + // This method only needs to be implemented if the AST source ever + // passes back decl sets as VisibleDeclaration objects. + return 0; + } + + virtual clang::Stmt * + GetExternalDeclStmt (uint64_t Offset) + { + // This operation is meant to be used via a LazyOffsetPtr. It only + // needs to be implemented if the AST source uses methods like + // FunctionDecl::setLazyBody when building decls. + return 0; + } + + virtual clang::Selector + GetExternalSelector (uint32_t ID) + { + // This operation only needs to be implemented if the AST source + // returns non-zero for GetNumKnownSelectors(). + return clang::Selector(); + } + + virtual uint32_t + GetNumExternalSelectors() + { + return 0; + } + + virtual clang::CXXBaseSpecifier * + GetExternalCXXBaseSpecifiers(uint64_t Offset) + { + return NULL; + } + + virtual void + MaterializeVisibleDecls (const clang::DeclContext *decl_ctx) + { + return; + } + + virtual clang::ExternalLoadResult + FindExternalLexicalDecls (const clang::DeclContext *decl_ctx, + bool (*isKindWeWant)(clang::Decl::Kind), + llvm::SmallVectorImpl &decls) + { + // This is used to support iterating through an entire lexical context, + // which isn't something the debugger should ever need to do. + return clang::ELR_Failure; + } + + virtual bool + FindExternalVisibleDeclsByName (const clang::DeclContext *decl_ctx, + clang::DeclarationName decl_name); + + virtual void + CompleteType (clang::TagDecl *tag_decl); + + virtual void + CompleteType (clang::ObjCInterfaceDecl *objc_decl); + + bool + layoutRecordType(const clang::RecordDecl *Record, + uint64_t &Size, + uint64_t &Alignment, + llvm::DenseMap &FieldOffsets, + llvm::DenseMap &BaseOffsets, + llvm::DenseMap &VirtualBaseOffsets); + void + SetExternalSourceCallbacks (CompleteTagDeclCallback tag_decl_callback, + CompleteObjCInterfaceDeclCallback objc_decl_callback, + FindExternalVisibleDeclsByNameCallback find_by_name_callback, + LayoutRecordTypeCallback layout_record_type_callback, + void *callback_baton) + { + m_callback_tag_decl = tag_decl_callback; + m_callback_objc_decl = objc_decl_callback; + m_callback_find_by_name = find_by_name_callback; + m_callback_layout_record_type = layout_record_type_callback; + m_callback_baton = callback_baton; + } + + void + RemoveExternalSourceCallbacks (void *callback_baton) + { + if (callback_baton == m_callback_baton) + { + m_callback_tag_decl = NULL; + m_callback_objc_decl = NULL; + m_callback_find_by_name = NULL; + m_callback_layout_record_type = NULL; + } + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ClangExternalASTSourceCallbacks can see and modify these + //------------------------------------------------------------------ + CompleteTagDeclCallback m_callback_tag_decl; + CompleteObjCInterfaceDeclCallback m_callback_objc_decl; + FindExternalVisibleDeclsByNameCallback m_callback_find_by_name; + LayoutRecordTypeCallback m_callback_layout_record_type; + void * m_callback_baton; +}; + +} // namespace lldb_private + +#endif // liblldb_ClangExternalASTSourceCallbacks_h_ diff --git a/include/lldb/Symbol/ClangExternalASTSourceCommon.h b/include/lldb/Symbol/ClangExternalASTSourceCommon.h new file mode 100644 index 00000000000..72d77e74ca9 --- /dev/null +++ b/include/lldb/Symbol/ClangExternalASTSourceCommon.h @@ -0,0 +1,188 @@ +//===-- ClangExternalASTSourceCommon.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangExternalASTSourceCommon_h +#define liblldb_ClangExternalASTSourceCommon_h + +// Clang headers like to use NDEBUG inside of them to enable/disable debug +// releated 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 +#endif + +#include "clang/AST/ExternalASTSource.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 +#endif + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/dwarf.h" + +namespace lldb_private { + +class ClangASTMetadata +{ +public: + ClangASTMetadata () : + m_user_id(0), + m_union_is_user_id(false), + m_union_is_isa_ptr(false), + m_has_object_ptr(false), + m_is_self (false), + m_is_dynamic_cxx (true) + { + } + + bool + GetIsDynamicCXXType () const + { + return m_is_dynamic_cxx; + } + + void + SetIsDynamicCXXType (bool b) + { + m_is_dynamic_cxx = b; + } + + void + SetUserID (lldb::user_id_t user_id) + { + m_user_id = user_id; + m_union_is_user_id = true; + m_union_is_isa_ptr = false; + } + + lldb::user_id_t + GetUserID () const + { + if (m_union_is_user_id) + return m_user_id; + else + return LLDB_INVALID_UID; + } + + void + SetISAPtr (uint64_t isa_ptr) + { + m_isa_ptr = isa_ptr; + m_union_is_user_id = false; + m_union_is_isa_ptr = true; + } + + uint64_t + GetISAPtr () const + { + if (m_union_is_isa_ptr) + return m_isa_ptr; + else + return 0; + } + + void + SetObjectPtrName(const char *name) + { + m_has_object_ptr = true; + if (strcmp (name, "self") == 0) + m_is_self = true; + else if (strcmp (name, "this") == 0) + m_is_self = false; + else + m_has_object_ptr = false; + } + + lldb::LanguageType + GetObjectPtrLanguage () const + { + if (m_has_object_ptr) + { + if (m_is_self) + return lldb::eLanguageTypeObjC; + else + return lldb::eLanguageTypeC_plus_plus; + } + return lldb::eLanguageTypeUnknown; + + } + const char * + GetObjectPtrName() const + { + if (m_has_object_ptr) + { + if (m_is_self) + return "self"; + else + return "this"; + } + else + return NULL; + } + + bool + HasObjectPtr() const + { + return m_has_object_ptr; + } + + void + Dump (Stream *s); + +private: + union + { + lldb::user_id_t m_user_id; + uint64_t m_isa_ptr; + }; + bool m_union_is_user_id : 1, + m_union_is_isa_ptr : 1, + m_has_object_ptr : 1, + m_is_self : 1, + m_is_dynamic_cxx : 1; + +}; + +class ClangExternalASTSourceCommon : public clang::ExternalASTSource +{ +public: + ClangExternalASTSourceCommon(); + ~ClangExternalASTSourceCommon(); + + virtual ClangASTMetadata *GetMetadata(const void *object); + virtual void SetMetadata(const void *object, ClangASTMetadata &metadata); + virtual bool HasMetadata(const void *object); +private: + typedef llvm::DenseMap MetadataMap; + + MetadataMap m_metadata; + uint64_t m_magic; ///< Because we don't have RTTI, we must take it + ///< on faith that any valid ExternalASTSource that + ///< we try to use the *Metadata APIs on inherits + ///< from ClangExternalASTSourceCommon. This magic + ///< number exists to enforce that. +}; + +} + +#endif diff --git a/include/lldb/Symbol/ClangNamespaceDecl.h b/include/lldb/Symbol/ClangNamespaceDecl.h new file mode 100644 index 00000000000..d10ab2a2966 --- /dev/null +++ b/include/lldb/Symbol/ClangNamespaceDecl.h @@ -0,0 +1,103 @@ +//===-- ClangNamespaceDecl.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ClangNamespaceDecl_h_ +#define liblldb_ClangNamespaceDecl_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Core/ClangForward.h" + +namespace lldb_private { + +class ClangNamespaceDecl +{ +public: + ClangNamespaceDecl () : + m_ast (NULL), + m_namespace_decl (NULL) + { + } + + ClangNamespaceDecl (clang::ASTContext *ast, clang::NamespaceDecl *namespace_decl) : + m_ast (ast), + m_namespace_decl (namespace_decl) + { + } + + ClangNamespaceDecl (const ClangNamespaceDecl &rhs) : + m_ast (rhs.m_ast), + m_namespace_decl (rhs.m_namespace_decl) + { + } + + const ClangNamespaceDecl & + operator = (const ClangNamespaceDecl &rhs) + { + m_ast = rhs.m_ast; + m_namespace_decl = rhs.m_namespace_decl; + return *this; + } + + //------------------------------------------------------------------ + /// Convert to bool operator. + /// + /// This allows code to check a ClangNamespaceDecl object to see if + /// it contains a valid namespace decl using code such as: + /// + /// @code + /// ClangNamespaceDecl ns_decl(...); + /// if (ns_decl) + /// { ... + /// @endcode + /// + /// @return + /// /b True this object contains a valid namespace decl, \b + /// false otherwise. + //------------------------------------------------------------------ + operator bool() const + { + return m_ast != NULL && m_namespace_decl != NULL; + } + + clang::ASTContext * + GetASTContext() const + { + return m_ast; + } + + void + SetASTContext (clang::ASTContext *ast) + { + m_ast = ast; + } + + clang::NamespaceDecl * + GetNamespaceDecl () const + { + return m_namespace_decl; + } + + void + SetNamespaceDecl (clang::NamespaceDecl *namespace_decl) + { + m_namespace_decl = namespace_decl; + } + + std::string + GetQualifiedName () const; + +protected: + clang::ASTContext *m_ast; + clang::NamespaceDecl *m_namespace_decl; +}; + + +} // namespace lldb_private + +#endif // #ifndef liblldb_ClangNamespaceDecl_h_ diff --git a/include/lldb/Symbol/CompileUnit.h b/include/lldb/Symbol/CompileUnit.h new file mode 100644 index 00000000000..5de93670c5a --- /dev/null +++ b/include/lldb/Symbol/CompileUnit.h @@ -0,0 +1,422 @@ +//===-- CompileUnit.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CompUnit_h_ +#define liblldb_CompUnit_h_ + +#include "lldb/lldb-enumerations.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { +//---------------------------------------------------------------------- +/// @class CompileUnit CompileUnit.h "lldb/Symbol/CompileUnit.h" +/// @brief A class that describes a compilation unit. +/// +/// A representation of a compilation unit, or compiled source file. +/// The UserID of the compile unit is specified by the SymbolFile +/// plug-in and can have any value as long as the value is unique +/// within the Module that owns this compile units. +/// +/// Each compile unit has a list of functions, global and static +/// variables, support file list (include files and inlined source +/// files), and a line table. +//---------------------------------------------------------------------- +class CompileUnit : + public std::enable_shared_from_this, + public ModuleChild, + public FileSpec, + public UserID, + public SymbolContextScope +{ +public: + //------------------------------------------------------------------ + /// Construct with a module, path, UID and language. + /// + /// Initialize the compile unit given the owning \a module, a path + /// to convert into a FileSpec, the SymbolFile plug-in supplied + /// \a uid, and the source language type. + /// + /// @param[in] module + /// The parent module that owns this compile unit. This value + /// must be a valid pointer value. + /// + /// @param[in] user_data + /// User data where the SymbolFile parser can store data. + /// + /// @param[in] pathname + /// The path to the source file for this compile unit. + /// + /// @param[in] uid + /// The user ID of the compile unit. This value is supplied by + /// the SymbolFile plug-in and should be a value that allows + /// the SymbolFile plug-in to easily locate and parse additional + /// information for the compile unit. + /// + /// @param[in] language + /// A language enumeration type that describes the main language + /// of this compile unit. + /// + /// @see lldb::LanguageType + //------------------------------------------------------------------ + CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, const char *pathname, lldb::user_id_t uid, lldb::LanguageType language); + + //------------------------------------------------------------------ + /// Construct with a module, file spec, UID and language. + /// + /// Initialize the compile unit given the owning \a module, a path + /// to convert into a FileSpec, the SymbolFile plug-in supplied + /// \a uid, and the source language type. + /// + /// @param[in] module + /// The parent module that owns this compile unit. This value + /// must be a valid pointer value. + /// + /// @param[in] user_data + /// User data where the SymbolFile parser can store data. + /// + /// @param[in] file_spec + /// The file specification for the source file of this compile + /// unit. + /// + /// @param[in] uid + /// The user ID of the compile unit. This value is supplied by + /// the SymbolFile plug-in and should be a value that allows + /// the plug-in to easily locate and parse + /// additional information for the compile unit. + /// + /// @param[in] language + /// A language enumeration type that describes the main language + /// of this compile unit. + /// + /// @see lldb::LanguageType + //------------------------------------------------------------------ + CompileUnit(const lldb::ModuleSP &module_sp, void *user_data, const FileSpec &file_spec, lldb::user_id_t uid, lldb::LanguageType language); + + //------------------------------------------------------------------ + /// Destructor + //------------------------------------------------------------------ + virtual + ~CompileUnit(); + + //------------------------------------------------------------------ + /// Add a function to this compile unit. + /// + /// Typically called by the SymbolFile plug-ins as they partially + /// parse the debug information. + /// + /// @param[in] function_sp + /// A shared pointer to the a Function object. + //------------------------------------------------------------------ + void + AddFunction(lldb::FunctionSP& function_sp); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual CompileUnit * + CalculateSymbolContextCompileUnit (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + lldb::LanguageType + GetLanguage(); + + void + SetLanguage(lldb::LanguageType language) + { + m_flags.Set(flagsParsedLanguage); + m_language = language; + } + + void + GetDescription(Stream *s, lldb::DescriptionLevel level) const; + + //------------------------------------------------------------------ + /// Get a shared pointer to a function in this compile unit by + /// index. + /// + /// Typically called when iterating though all functions in a + /// compile unit after all functions have been parsed. This provides + /// raw access to the function shared pointer list and will not + /// cause the SymbolFile plug-in to parse any unparsed functions. + /// + /// @param[in] idx + /// An index into the function list. + /// + /// @return + /// A shared pointer to a function that might contain a NULL + /// Function class pointer. + //------------------------------------------------------------------ + lldb::FunctionSP + GetFunctionAtIndex (size_t idx); + + //------------------------------------------------------------------ + /// Dump the compile unit contents to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] show_context + /// If \b true, variables will dump their symbol context + /// information. + //------------------------------------------------------------------ + void + Dump (Stream *s, bool show_context) const; + + //------------------------------------------------------------------ + /// Find the line entry by line and optional inlined file spec. + /// + /// Finds the first line entry that has an index greater than + /// \a start_idx that matches \a line. If \a file_spec_ptr + /// is NULL, then the search matches line entries whose file matches + /// the file for the compile unit. If \a file_spec_ptr is + /// not NULL, line entries must match the specified file spec (for + /// inlined line table entries). + /// + /// Multiple calls to this function can find all entries that match + /// a given file and line by starting with \a start_idx equal to zero, + /// and calling this function back with the return valeu + 1. + /// + /// @param[in] start_idx + /// The zero based index at which to start looking for matches. + /// + /// @param[in] line + /// The line number to search for. + /// + /// @param[in] file_spec_ptr + /// If non-NULL search for entries that match this file spec, + /// else if NULL, search for line entries that match the compile + /// unit file. + /// + /// @param[in] exact + /// If \btrue match only if there is a line table entry for this line number. + /// If \bfalse, find the line table entry equal to or after this line number. + /// + /// @param[out] line_entry + /// If non-NULL, a copy of the line entry that was found. + /// + /// @return + /// The zero based index of a matching line entry, or UINT32_MAX + /// if no matching line entry is found. + //------------------------------------------------------------------ + uint32_t + FindLineEntry (uint32_t start_idx, + uint32_t line, + const FileSpec* file_spec_ptr, + bool exact, + LineEntry *line_entry); + + //------------------------------------------------------------------ + /// Get the line table for the compile unit. + /// + /// Called by clients and the SymbolFile plug-in. The SymbolFile + /// plug-ins use this function to determine if the line table has + /// be parsed yet. Clients use this function to get the line table + /// from a compile unit. + /// + /// @return + /// The line table object pointer, or NULL if this line table + /// hasn't been parsed yet. + //------------------------------------------------------------------ + LineTable* + GetLineTable (); + + //------------------------------------------------------------------ + /// Get the compile unit's support file list. + /// + /// The support file list is used by the line table, and any objects + /// that have valid Declaration objects. + /// + /// @return + /// A support file list object. + //------------------------------------------------------------------ + FileSpecList& + GetSupportFiles (); + + //------------------------------------------------------------------ + /// Get the SymbolFile plug-in user data. + /// + /// SymbolFile plug-ins can store user data to internal state or + /// objects to quickly allow them to parse more information for a + /// given object. + /// + /// @return + /// The user data stored with the CompileUnit when it was + /// constructed. + //------------------------------------------------------------------ + void * + GetUserData () const; + + //------------------------------------------------------------------ + /// Get the variable list for a compile unit. + /// + /// Called by clients to get the variable list for a compile unit. + /// The variable list will contain all global and static variables + /// that were defined at the compile unit level. + /// + /// @param[in] can_create + /// If \b true, the variable list will be parsed on demand. If + /// \b false, the current variable list will be returned even + /// if it contains a NULL VariableList object (typically + /// called by dumping routines that want to display only what + /// has currently been parsed). + /// + /// @return + /// A shared pointer to a variable list, that can contain NULL + /// VariableList pointer if there are no global or static + /// variables. + //------------------------------------------------------------------ + lldb::VariableListSP + GetVariableList (bool can_create); + + //------------------------------------------------------------------ + /// Finds a function by user ID. + /// + /// Typically used by SymbolFile plug-ins when partially parsing + /// the debug information to see if the function has been parsed + /// yet. + /// + /// @param[in] uid + /// The user ID of the function to find. This value is supplied + /// by the SymbolFile plug-in and should be a value that + /// allows the plug-in to easily locate and parse additional + /// information in the function. + /// + /// @return + /// A shared pointer to the function object that might contain + /// a NULL Function pointer. + //------------------------------------------------------------------ + lldb::FunctionSP + FindFunctionByUID (lldb::user_id_t uid); + + //------------------------------------------------------------------ + /// Set the line table for the compile unit. + /// + /// Called by the SymbolFile plug-in when if first parses the line + /// table and hands ownership of the line table to this object. The + /// compile unit owns the line table object and will delete the + /// object when it is deleted. + /// + /// @param[in] line_table + /// A line table object pointer that this object now owns. + //------------------------------------------------------------------ + void + SetLineTable(LineTable* line_table); + + //------------------------------------------------------------------ + /// Set accessor for the variable list. + /// + /// Called by the SymbolFile plug-ins after they have parsed the + /// variable lists and are ready to hand ownership of the list over + /// to this object. + /// + /// @param[in] variable_list_sp + /// A shared pointer to a VariableList. + //------------------------------------------------------------------ + void + SetVariableList (lldb::VariableListSP& variable_list_sp); + + //------------------------------------------------------------------ + /// Resolve symbol contexts by file and line. + /// + /// Given a file in \a file_spec, and a line number, find all + /// instances and append them to the supplied symbol context list + /// \a sc_list. + /// + /// @param[in] file_spec + /// A file specification. If \a file_spec contains no directory + /// information, only the basename will be used when matching + /// contexts. If the directory in \a file_spec is valid, a + /// complete file specification match will be performed. + /// + /// @param[in] line + /// The line number to match against the compile unit's line + /// tables. + /// + /// @param[in] check_inlines + /// If \b true this function will also match any inline + /// file and line matches. If \b false, the compile unit's + /// file specification must match \a file_spec for any matches + /// to be returned. + /// + /// @param[in] exact + /// If true, only resolve the context if \a line exists in the line table. + /// If false, resolve the context to the closest line greater than \a line + /// in the line table. + /// + /// @param[in] resolve_scope + /// For each matching line entry, this bitfield indicates what + /// values within each SymbolContext that gets added to \a + /// sc_list will be resolved. See the SymbolContext::Scope + /// enumeration for a list of all available bits that can be + /// resolved. Only SymbolContext entries that can be resolved + /// using a LineEntry base address will be able to be resolved. + /// + /// @param[out] sc_list + /// A SymbolContext list class that willl get any matching + /// entries appended to. + /// + /// @return + /// The number of new matches that were added to \a sc_list. + /// + /// @see enum SymbolContext::Scope + //------------------------------------------------------------------ + uint32_t + ResolveSymbolContext (const FileSpec& file_spec, + uint32_t line, + bool check_inlines, + bool exact, + uint32_t resolve_scope, + SymbolContextList &sc_list); + + +protected: + void *m_user_data; ///< User data for the SymbolFile parser to store information into. + lldb::LanguageType m_language; ///< The programming language enumeration value. + Flags m_flags; ///< Compile unit flags that help with partial parsing. + std::vector m_functions; ///< The sparsely populated list of shared pointers to functions + ///< that gets populated as functions get partially parsed. + FileSpecList m_support_files; ///< Files associated with this compile unit's line table and declarations. + std::unique_ptr m_line_table_ap; ///< Line table that will get parsed on demand. + lldb::VariableListSP m_variables; ///< Global and static variable list that will get parsed on demand. + +private: + enum + { + flagsParsedAllFunctions = (1u << 0), ///< Have we already parsed all our functions + flagsParsedVariables = (1u << 1), ///< Have we already parsed globals and statics? + flagsParsedSupportFiles = (1u << 2), ///< Have we already parsed the support files for this compile unit? + flagsParsedLineTable = (1u << 3), ///< Have we parsed the line table already? + flagsParsedLanguage = (1u << 4) ///< Have we parsed the line table already? + }; + + DISALLOW_COPY_AND_ASSIGN (CompileUnit); +}; + +} // namespace lldb_private + +#endif // liblldb_CompUnit_h_ diff --git a/include/lldb/Symbol/DWARFCallFrameInfo.h b/include/lldb/Symbol/DWARFCallFrameInfo.h new file mode 100644 index 00000000000..13a14f8c404 --- /dev/null +++ b/include/lldb/Symbol/DWARFCallFrameInfo.h @@ -0,0 +1,150 @@ +//===-- DWARFCallFrameInfo.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DWARFCallFrameInfo_h_ +#define liblldb_DWARFCallFrameInfo_h_ + +#include + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/VMRange.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// DWARFCallFrameInfo is a class which can read eh_frame and DWARF +// Call Frame Information FDEs. It stores little information internally. +// Only two APIs are exported - one to find the high/low pc values +// of a function given a text address via the information in the +// eh_frame / debug_frame, and one to generate an UnwindPlan based +// on the FDE in the eh_frame / debug_frame section. + +class DWARFCallFrameInfo +{ +public: + + DWARFCallFrameInfo (ObjectFile& objfile, + lldb::SectionSP& section, + lldb::RegisterKind reg_kind, + bool is_eh_frame); + + ~DWARFCallFrameInfo(); + + // Locate an AddressRange that includes the provided Address in this + // object's eh_frame/debug_info + // Returns true if a range is found to cover that address. + bool + GetAddressRange (Address addr, AddressRange &range); + + // Return an UnwindPlan based on the call frame information encoded + // in the FDE of this DWARFCallFrameInfo section. + bool + GetUnwindPlan (Address addr, UnwindPlan& unwind_plan); + + typedef RangeVector FunctionAddressAndSizeVector; + + //------------------------------------------------------------------ + // Build a vector of file address and size for all functions in this Module + // based on the eh_frame FDE entries. + // + // The eh_frame information can be a useful source of file address and size of + // the functions in a Module. Often a binary's non-exported symbols are stripped + // before shipping so lldb won't know the start addr / size of many functions + // in the Module. But the eh_frame can help to give the addresses of these + // stripped symbols, at least. + // + // @param[out] function_info + // A vector provided by the caller is filled out. May be empty if no FDEs/no eh_frame + // is present in this Module. + + void + GetFunctionAddressAndSizeVector (FunctionAddressAndSizeVector &function_info); + +private: + enum + { + CFI_AUG_MAX_SIZE = 8, + CFI_HEADER_SIZE = 8 + }; + + struct CIE + { + dw_offset_t cie_offset; + uint8_t version; + char augmentation[CFI_AUG_MAX_SIZE]; // This is typically empty or very short. + uint32_t code_align; + int32_t data_align; + uint32_t return_addr_reg_num; + dw_offset_t inst_offset; // offset of CIE instructions in mCFIData + uint32_t inst_length; // length of CIE instructions in mCFIData + uint8_t ptr_encoding; + lldb_private::UnwindPlan::Row initial_row; + + CIE(dw_offset_t offset) : cie_offset(offset), version (-1), code_align (0), + data_align (0), return_addr_reg_num (LLDB_INVALID_REGNUM), inst_offset (0), + inst_length (0), ptr_encoding (0), initial_row() {} + }; + + typedef std::shared_ptr CIESP; + + typedef std::map cie_map_t; + + // Start address (file address), size, offset of FDE location + // used for finding an FDE for a given File address; the start address field is + // an offset into an individual Module. + typedef RangeDataVector FDEEntryMap; + + bool + IsEHFrame() const; + + bool + GetFDEEntryByFileAddress (lldb::addr_t file_offset, FDEEntryMap::Entry& fde_entry); + + void + GetFDEIndex (); + + bool + FDEToUnwindPlan (uint32_t offset, Address startaddr, UnwindPlan& unwind_plan); + + const CIE* + GetCIE(dw_offset_t cie_offset); + + void + GetCFIData(); + + ObjectFile& m_objfile; + lldb::SectionSP m_section_sp; + lldb::RegisterKind m_reg_kind; + Flags m_flags; + cie_map_t m_cie_map; + + DataExtractor m_cfi_data; + bool m_cfi_data_initialized; // only copy the section into the DE once + + FDEEntryMap m_fde_index; + bool m_fde_index_initialized; // only scan the section for FDEs once + Mutex m_fde_index_mutex; // and isolate the thread that does it + + bool m_is_eh_frame; + + CIESP + ParseCIE (const uint32_t cie_offset); + +}; + +} // namespace lldb_private + +#endif // liblldb_DWARFCallFrameInfo_h_ diff --git a/include/lldb/Symbol/Declaration.h b/include/lldb/Symbol/Declaration.h new file mode 100644 index 00000000000..f014571595f --- /dev/null +++ b/include/lldb/Symbol/Declaration.h @@ -0,0 +1,278 @@ +//===-- Declaration.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Declaration_h_ +#define liblldb_Declaration_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class Declaration Declaration.h "lldb/Symbol/Declaration.h" +/// @brief A class that describes the declaration location of a +/// lldb object. +/// +/// The declarations include the file specification, line number, and +/// the column info and can help track where functions, blocks, inlined +/// functions, types, variables, any many other debug core objects were +/// declared. +//---------------------------------------------------------------------- +class Declaration +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + //------------------------------------------------------------------ + Declaration () : + m_file (), + m_line (0) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column (0) +#endif + { + } + + + //------------------------------------------------------------------ + /// Construct with file specification, and optional line and column. + /// + /// @param[in] file_spec + /// The file specification that describes where this was + /// declared. + /// + /// @param[in] line + /// The line number that describes where this was declared. Set + /// to zero if there is no line number information. + /// + /// @param[in] column + /// The column number that describes where this was declared. + /// Set to zero if there is no column number information. + //------------------------------------------------------------------ + Declaration (const FileSpec& file_spec, uint32_t line = 0, uint32_t column = 0) : + m_file (file_spec), + m_line (line) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column (column) +#endif + { + } + + //------------------------------------------------------------------ + /// Construct with a reference to another Declaration object. + //------------------------------------------------------------------ + Declaration (const Declaration& rhs) : + m_file (rhs.m_file), + m_line (rhs.m_line) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column (rhs.m_column) +#endif + { + + } + + //------------------------------------------------------------------ + /// Construct with a pointer to another Declaration object. + //------------------------------------------------------------------ + Declaration(const Declaration* decl_ptr) : + m_file(), + m_line(0) +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + ,m_column(0) +#endif + { + if (decl_ptr) + *this = *decl_ptr; + } + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the file specification to be empty, and the line and column + /// to zero. + //------------------------------------------------------------------ + void + Clear () + { + m_file.Clear(); + m_line= 0; +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + m_column = 0; +#endif + } + + //------------------------------------------------------------------ + /// Compare two declaration objects. + /// + /// Compares the two file specifications from \a lhs and \a rhs. If + /// the file specifications are equal, then continue to compare the + /// line number and column numbers respectively. + /// + /// @param[in] lhs + /// The Left Hand Side const Declaration object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const Declaration object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const Declaration& lhs, const Declaration& rhs); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, bool show_fullpaths) const; + + bool + DumpStopContext (Stream *s, bool show_fullpaths) const; + //------------------------------------------------------------------ + /// Get accessor for the declaration column number. + /// + /// @return + /// Non-zero indicates a valid column number, zero indicates no + /// column information is available. + //------------------------------------------------------------------ + uint32_t + GetColumn () const + { +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + return m_column; +#else + return 0; +#endif + } + + //------------------------------------------------------------------ + /// Get accessor for file specification. + /// + /// @return + /// A reference to the file specification object. + //------------------------------------------------------------------ + FileSpec& + GetFile () + { + return m_file; + } + + //------------------------------------------------------------------ + /// Get const accessor for file specification. + /// + /// @return + /// A const reference to the file specification object. + //------------------------------------------------------------------ + const FileSpec& + GetFile () const + { + return m_file; + } + + //------------------------------------------------------------------ + /// Get accessor for the declaration line number. + /// + /// @return + /// Non-zero indicates a valid line number, zero indicates no + /// line information is available. + //------------------------------------------------------------------ + uint32_t + GetLine () const + { + return m_line; + } + + + bool + IsValid() const + { + return m_file && m_line != 0; + } + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + + //------------------------------------------------------------------ + /// Set accessor for the declaration column number. + /// + /// @param[in] column + /// Non-zero indicates a valid column number, zero indicates no + /// column information is available. + //------------------------------------------------------------------ + void + SetColumn (uint32_t column) + { +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + m_column = col; +#endif + } + + //------------------------------------------------------------------ + /// Set accessor for the declaration file specification. + /// + /// @param[in] file_spec + /// The new declaration file specifciation. + //------------------------------------------------------------------ + void + SetFile (const FileSpec& file_spec) + { + m_file = file_spec; + } + + //------------------------------------------------------------------ + /// Set accessor for the declaration line number. + /// + /// @param[in] line + /// Non-zero indicates a valid line number, zero indicates no + /// line information is available. + //------------------------------------------------------------------ + void + SetLine (uint32_t line) + { + m_line = line; + } +protected: + //------------------------------------------------------------------ + /// Member variables. + //------------------------------------------------------------------ + FileSpec m_file; ///< The file specification that points to the + ///< source file where the declaration occurred. + uint32_t m_line; ///< Non-zero values indicates a valid line number, + ///< zero indicates no line number information is available. +#ifdef LLDB_ENABLE_DECLARATION_COLUMNS + uint32_t m_column; ///< Non-zero values indicates a valid column number, + ///< zero indicates no column information is available. +#endif +}; + +bool +operator == (const Declaration &lhs, const Declaration &rhs); + +} // namespace lldb_private + +#endif // liblldb_Declaration_h_ diff --git a/include/lldb/Symbol/FuncUnwinders.h b/include/lldb/Symbol/FuncUnwinders.h new file mode 100644 index 00000000000..fa48dc27e12 --- /dev/null +++ b/include/lldb/Symbol/FuncUnwinders.h @@ -0,0 +1,106 @@ +#ifndef liblldb_FuncUnwinders_h +#define liblldb_FuncUnwinders_h + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class UnwindTable; + +class FuncUnwinders +{ +public: + // FuncUnwinders objects are used to track UnwindPlans for a function + // (named or not - really just an address range) + + // We'll record three different UnwindPlans for each address range: + // 1. Unwinding from a call site (a valid exception throw location) + // This is often sourced from the eh_frame exception handling info + // 2. Unwinding from a non-call site (any location in the function) + // This is often done by analyzing the function prologue assembly + // langauge instructions + // 3. A fast unwind method for this function which only retrieves a + // limited set of registers necessary to walk the stack + // 4. An architectural default unwind plan when none of the above are + // available for some reason. + + // Additionally, FuncUnwinds object can be asked where the prologue + // instructions are finished for migrating breakpoints past the + // stack frame setup instructions when we don't have line table information. + + FuncUnwinders (lldb_private::UnwindTable& unwind_table, lldb_private::UnwindAssembly *assembly_profiler, AddressRange range); + + ~FuncUnwinders (); + + // current_offset is the byte offset into the function. + // 0 means no instructions have executed yet. -1 means the offset is unknown. + // On architectures where the pc points to the next instruction that will execute, this + // offset value will have already been decremented by 1 to stay within the bounds of the + // correct function body. + lldb::UnwindPlanSP + GetUnwindPlanAtCallSite (int current_offset); + + lldb::UnwindPlanSP + GetUnwindPlanAtNonCallSite (lldb_private::Thread& thread); + + lldb::UnwindPlanSP + GetUnwindPlanFastUnwind (lldb_private::Thread& Thread); + + lldb::UnwindPlanSP + GetUnwindPlanArchitectureDefault (lldb_private::Thread& thread); + + lldb::UnwindPlanSP + GetUnwindPlanArchitectureDefaultAtFunctionEntry (lldb_private::Thread& thread); + + Address& + GetFirstNonPrologueInsn (Target& target); + + const Address& + GetFunctionStartAddress () const; + + bool + ContainsAddress (const Address& addr) const + { + return m_range.ContainsFileAddress (addr); + } + + // When we're doing an unwind using the UnwindPlanAtNonCallSite and we find an + // impossible unwind condition, we know that the UnwindPlan is invalid. Calling + // this method on the FuncUnwinder will tell it to replace that UnwindPlan with + // the architectural default UnwindPlan so hopefully our stack walk will get past + // this frame. + void + InvalidateNonCallSiteUnwindPlan (lldb_private::Thread& Thread); + +private: + UnwindTable& m_unwind_table; + UnwindAssembly *m_assembly_profiler; + AddressRange m_range; + + Mutex m_mutex; + lldb::UnwindPlanSP m_unwind_plan_call_site_sp; + lldb::UnwindPlanSP m_unwind_plan_non_call_site_sp; + lldb::UnwindPlanSP m_unwind_plan_fast_sp; + lldb::UnwindPlanSP m_unwind_plan_arch_default_sp; + lldb::UnwindPlanSP m_unwind_plan_arch_default_at_func_entry_sp; + + bool m_tried_unwind_at_call_site:1, + m_tried_unwind_at_non_call_site:1, + m_tried_unwind_fast:1, + m_tried_unwind_arch_default:1, + m_tried_unwind_arch_default_at_func_entry:1; + + + Address m_first_non_prologue_insn; + + DISALLOW_COPY_AND_ASSIGN (FuncUnwinders); + +}; // class FuncUnwinders + +} // namespace lldb_private + + +#endif //liblldb_FuncUnwinders_h diff --git a/include/lldb/Symbol/Function.h b/include/lldb/Symbol/Function.h new file mode 100644 index 00000000000..787f81c5ad2 --- /dev/null +++ b/include/lldb/Symbol/Function.h @@ -0,0 +1,638 @@ +//===-- Function.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Function_h_ +#define liblldb_Function_h_ + +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Declaration.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/UserID.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class FunctionInfo Function.h "lldb/Symbol/Function.h" +/// @brief A class that contains generic function information. +/// +/// This provides generic function information that gets resused between +/// inline functions and function types. +//---------------------------------------------------------------------- +class FunctionInfo +{ +public: + //------------------------------------------------------------------ + /// Construct with the function method name and optional declaration + /// information. + /// + /// @param[in] name + /// A C string name for the method name for this function. This + /// value should not be the mangled named, but the simple method + /// name. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + //------------------------------------------------------------------ + FunctionInfo (const char *name, const Declaration *decl_ptr); + + //------------------------------------------------------------------ + /// Construct with the function method name and optional declaration + /// information. + /// + /// @param[in] name + /// A name for the method name for this function. This value + /// should not be the mangled named, but the simple method name. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + //------------------------------------------------------------------ + FunctionInfo (const ConstString& name, const Declaration *decl_ptr); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since classes inherit from this class. + //------------------------------------------------------------------ + virtual + ~FunctionInfo (); + + //------------------------------------------------------------------ + /// Compare two function information objects. + /// + /// First compares the method names, and if equal, then compares + /// the declaration information. + /// + /// @param[in] lhs + /// The Left Hand Side const FunctionInfo object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const FunctionInfo object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const FunctionInfo& lhs, const FunctionInfo& rhs); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, bool show_fullpaths) const; + + //------------------------------------------------------------------ + /// Get accessor for the declaration information. + /// + /// @return + /// A reference to the declaration object. + //------------------------------------------------------------------ + Declaration& + GetDeclaration (); + + //------------------------------------------------------------------ + /// Get const accessor for the declaration information. + /// + /// @return + /// A const reference to the declaration object. + //------------------------------------------------------------------ + const Declaration& + GetDeclaration () const; + + //------------------------------------------------------------------ + /// Get accessor for the method name. + /// + /// @return + /// A const reference to the method name object. + //------------------------------------------------------------------ + const ConstString& + GetName () const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + virtual size_t + MemorySize () const; + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + ConstString m_name; ///< Function method name (not a mangled name). + Declaration m_declaration; ///< Information describing where this function information was defined. +}; + + +//---------------------------------------------------------------------- +/// @class InlineFunctionInfo Function.h "lldb/Symbol/Function.h" +/// @brief A class that describes information for an inlined function. +//---------------------------------------------------------------------- +class InlineFunctionInfo : public FunctionInfo +{ +public: + //------------------------------------------------------------------ + /// Construct with the function method name, mangled name, and + /// optional declaration information. + /// + /// @param[in] name + /// A C string name for the method name for this function. This + /// value should not be the mangled named, but the simple method + /// name. + /// + /// @param[in] mangled + /// A C string name for the mangled name for this function. This + /// value can be NULL if there is no mangled information. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + /// + /// @param[in] call_decl_ptr + /// Optional calling location declaration information that + /// describes from where this inlined function was called. + //------------------------------------------------------------------ + InlineFunctionInfo(const char *name, const char *mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr); + + //------------------------------------------------------------------ + /// Construct with the function method name, mangled name, and + /// optional declaration information. + /// + /// @param[in] name + /// A name for the method name for this function. This value + /// should not be the mangled named, but the simple method name. + /// + /// @param[in] mangled + /// A name for the mangled name for this function. This value + /// can be empty if there is no mangled information. + /// + /// @param[in] decl_ptr + /// Optional declaration information that describes where the + /// function was declared. This can be NULL. + /// + /// @param[in] call_decl_ptr + /// Optional calling location declaration information that + /// describes from where this inlined function was called. + //------------------------------------------------------------------ + InlineFunctionInfo(const ConstString& name, const Mangled &mangled, const Declaration *decl_ptr, const Declaration *call_decl_ptr); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~InlineFunctionInfo(); + + //------------------------------------------------------------------ + /// Compare two inlined function information objects. + /// + /// First compares the FunctionInfo objects, and if equal, + /// compares the mangled names. + /// + /// @param[in] lhs + /// The Left Hand Side const InlineFunctionInfo object + /// reference. + /// + /// @param[in] rhs + /// The Right Hand Side const InlineFunctionInfo object + /// reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + int + Compare(const InlineFunctionInfo& lhs, const InlineFunctionInfo& rhs); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump(Stream *s, bool show_fullpaths) const; + + void + DumpStopContext (Stream *s) const; + + const ConstString & + GetName () const; + + //------------------------------------------------------------------ + /// Get accessor for the call site declaration information. + /// + /// @return + /// A reference to the declaration object. + //------------------------------------------------------------------ + Declaration& + GetCallSite (); + + //------------------------------------------------------------------ + /// Get const accessor for the call site declaration information. + /// + /// @return + /// A const reference to the declaration object. + //------------------------------------------------------------------ + const Declaration& + GetCallSite () const; + + //------------------------------------------------------------------ + /// Get accessor for the mangled name object. + /// + /// @return + /// A reference to the mangled name object. + //------------------------------------------------------------------ + Mangled& + GetMangled(); + + //------------------------------------------------------------------ + /// Get const accessor for the mangled name object. + /// + /// @return + /// A const reference to the mangled name object. + //------------------------------------------------------------------ + const Mangled& + GetMangled() const; + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + virtual size_t + MemorySize() const; + +private: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Mangled m_mangled; ///< Mangled inlined function name (can be empty if there is no mangled information). + Declaration m_call_decl; +}; + +//---------------------------------------------------------------------- +/// @class Function Function.h "lldb/Symbol/Function.h" +/// @brief A class that describes a function. +/// +/// Functions belong to CompileUnit objects (Function::m_comp_unit), +/// have unique user IDs (Function::UserID), know how to reconstruct +/// their symbol context (Function::SymbolContextScope), have a +/// specific function type (Function::m_type_uid), have a simple +/// method name (FunctionInfo::m_name), be declared at a specific +/// location (FunctionInfo::m_declaration), possibly have mangled +/// names (Function::m_mangled), an optional return type +/// (Function::m_type), and contains lexical blocks +/// (Function::m_blocks). +/// +/// The function inforation is split into a few pieces: +/// @li The concrete instance information +/// @li The abstract information +/// +/// The abstract information is found in the function type (Type) that +/// describes a function information, return type and parameter types. +/// +/// The concreate information is the address range information and +/// specific locations for an instance of this function. +//---------------------------------------------------------------------- +class Function : + public UserID, + public SymbolContextScope +{ +public: + //------------------------------------------------------------------ + /// Construct with a compile unit, function UID, function type UID, + /// optional mangled name, function type, and a section offset + /// based address range. + /// + /// @param[in] comp_unit + /// The compile unit to which this function belongs. + /// + /// @param[in] func_uid + /// The UID for this function. This value is provided by the + /// SymbolFile plug-in and can be any value that allows + /// the plug-in to quickly find and parse more detailed + /// information when and if more information is needed. + /// + /// @param[in] func_type_uid + /// The type UID for the function Type to allow for lazy type + /// parsing from the debug information. + /// + /// @param[in] mangled + /// The optional mangled name for this function. If empty, there + /// is no mangled information. + /// + /// @param[in] func_type + /// The optional function type. If NULL, the function type will + /// be parsed on demand when accessed using the + /// Function::GetType() function by asking the SymbolFile + /// plug-in to get the type for \a func_type_uid. + /// + /// @param[in] range + /// The section offset based address for this function. + //------------------------------------------------------------------ + Function ( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t func_type_uid, + const Mangled &mangled, + Type * func_type, + const AddressRange& range); + + //------------------------------------------------------------------ + /// Construct with a compile unit, function UID, function type UID, + /// optional mangled name, function type, and a section offset + /// based address range. + /// + /// @param[in] comp_unit + /// The compile unit to which this function belongs. + /// + /// @param[in] func_uid + /// The UID for this function. This value is provided by the + /// SymbolFile plug-in and can be any value that allows + /// the plug-in to quickly find and parse more detailed + /// information when and if more information is needed. + /// + /// @param[in] func_type_uid + /// The type UID for the function Type to allow for lazy type + /// parsing from the debug information. + /// + /// @param[in] mangled + /// The optional mangled name for this function. If empty, there + /// is no mangled information. + /// + /// @param[in] func_type + /// The optional function type. If NULL, the function type will + /// be parsed on demand when accessed using the + /// Function::GetType() function by asking the SymbolFile + /// plug-in to get the type for \a func_type_uid. + /// + /// @param[in] range + /// The section offset based address for this function. + //------------------------------------------------------------------ + Function ( + CompileUnit *comp_unit, + lldb::user_id_t func_uid, + lldb::user_id_t func_type_uid, + const char *mangled, + Type * func_type, + const AddressRange& range); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~Function (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext(SymbolContext* sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual CompileUnit * + CalculateSymbolContextCompileUnit (); + + virtual Function * + CalculateSymbolContextFunction (); + + const AddressRange & + GetAddressRange() + { + return m_range; + } + + //------------------------------------------------------------------ + /// Find the file and line number of the source location of the start + /// of the function. This will use the declaration if present and fall + /// back on the line table if that fails. So there may NOT be a line + /// table entry for this source file/line combo. + /// + /// @param[out] source_file + /// The source file. + /// + /// @param[out] line_no + /// The line number. + //------------------------------------------------------------------ + void + GetStartLineSourceInfo (FileSpec &source_file, uint32_t &line_no); + + //------------------------------------------------------------------ + /// Find the file and line number of the source location of the end + /// of the function. + /// + /// + /// @param[out] source_file + /// The source file. + /// + /// @param[out] line_no + /// The line number. + //------------------------------------------------------------------ + void + GetEndLineSourceInfo (FileSpec &source_file, uint32_t &line_no); + + //------------------------------------------------------------------ + /// Get accessor for the block list. + /// + /// @return + /// The block list object that describes all lexical blocks + /// in the function. + /// + /// @see BlockList + //------------------------------------------------------------------ + Block& + GetBlock (bool can_create); + + //------------------------------------------------------------------ + /// Get accessor for the compile unit that owns this function. + /// + /// @return + /// A compile unit object pointer. + //------------------------------------------------------------------ + CompileUnit* + GetCompileUnit(); + + //------------------------------------------------------------------ + /// Get const accessor for the compile unit that owns this function. + /// + /// @return + /// A const compile unit object pointer. + //------------------------------------------------------------------ + const CompileUnit* + GetCompileUnit() const; + + void + GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target); + + //------------------------------------------------------------------ + /// Get accessor for the frame base location. + /// + /// @return + /// A location expression that describes the function frame + /// base. + //------------------------------------------------------------------ + DWARFExpression & + GetFrameBaseExpression() + { + return m_frame_base; + } + + //------------------------------------------------------------------ + /// Get const accessor for the frame base location. + /// + /// @return + /// A const compile unit object pointer. + //------------------------------------------------------------------ + const DWARFExpression & + GetFrameBaseExpression() const + { + return m_frame_base; + } + + const ConstString & + GetName() const + { + return m_mangled.GetName(); + } + + const Mangled & + GetMangled() const + { + return m_mangled; + } + + //------------------------------------------------------------------ + /// Get the DeclContext for this function, if available. + /// + /// @return + /// The DeclContext, or NULL if none exists. + //------------------------------------------------------------------ + clang::DeclContext * + GetClangDeclContext(); + + //------------------------------------------------------------------ + /// Get accessor for the type that describes the function + /// return value type, and paramter types. + /// + /// @return + /// A type object pointer. + //------------------------------------------------------------------ + Type* + GetType(); + + //------------------------------------------------------------------ + /// Get const accessor for the type that describes the function + /// return value type, and paramter types. + /// + /// @return + /// A const type object pointer. + //------------------------------------------------------------------ + const Type* + GetType() const; + + ClangASTType + GetClangType (); + + uint32_t + GetPrologueByteSize (); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] show_context + /// If \b true, variables will dump their symbol context + /// information. + //------------------------------------------------------------------ + void + Dump(Stream *s, bool show_context) const; + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext(Stream *s); + + //------------------------------------------------------------------ + /// Get the memory cost of this object. + /// + /// @return + /// The number of bytes that this object occupies in memory. + /// The returned value does not include the bytes for any + /// shared string values. + /// + /// @see ConstString::StaticMemorySize () + //------------------------------------------------------------------ + size_t + MemorySize () const; + +protected: + + enum + { + flagsCalculatedPrologueSize = (1 << 0) ///< Have we already tried to calculate the prologue size? + }; + + + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + CompileUnit *m_comp_unit; ///< The compile unit that owns this function. + lldb::user_id_t m_type_uid; ///< The user ID of for the prototype Type for this function. + Type * m_type; ///< The function prototype type for this function that include the function info (FunctionInfo), return type and parameters. + Mangled m_mangled; ///< The mangled function name if any, if empty, there is no mangled information. + Block m_block; ///< All lexical blocks contained in this function. + AddressRange m_range; ///< The function address range that covers the widest range needed to contain all blocks + DWARFExpression m_frame_base; ///< The frame base expression for variables that are relative to the frame pointer. + Flags m_flags; + uint32_t m_prologue_byte_size; ///< Compute the prologue size once and cache it +private: + DISALLOW_COPY_AND_ASSIGN(Function); +}; + +} // namespace lldb_private + +#endif // liblldb_Function_h_ diff --git a/include/lldb/Symbol/LineEntry.h b/include/lldb/Symbol/LineEntry.h new file mode 100644 index 00000000000..d7750cd3491 --- /dev/null +++ b/include/lldb/Symbol/LineEntry.h @@ -0,0 +1,174 @@ +//===-- LineEntry.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LineEntry_h_ +#define liblldb_LineEntry_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Host/FileSpec.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class LineEntry LineEntry.h "lldb/Symbol/LineEntry.h" +/// @brief A line table entry class. +//---------------------------------------------------------------------- +struct LineEntry +{ + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all member variables to invalid values. + //------------------------------------------------------------------ + LineEntry (); + + LineEntry + ( + const lldb::SectionSP §ion_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 + ); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears all member variables to invalid values. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] comp_unit + /// The compile unit object that contains the support file + /// list so the line entry can dump the file name (since this + /// object contains a file index into the support file list). + /// + /// @param[in] show_file + /// If \b true, display the filename with the line entry which + /// requires that the compile unit object \a comp_unit be a + /// valid pointer. + /// + /// @param[in] style + /// The display style for the section offset address. + /// + /// @return + /// Returns \b true if the address was able to be displayed + /// using \a style. File and load addresses may be unresolved + /// and it may not be possible to display a valid address value. + /// Returns \b false if the address was not able to be properly + /// dumped. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + bool + Dump (Stream *s, Target *target, bool show_file, Address::DumpStyle style, Address::DumpStyle fallback_style, bool show_range) const; + + bool + GetDescription (Stream *s, + lldb::DescriptionLevel level, + CompileUnit* cu, + Target *target, + bool show_address_only) const; + + //------------------------------------------------------------------ + /// Dumps information specific to a process that stops at this + /// line entry to the supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] comp_unit + /// The compile unit object that contains the support file + /// list so the line entry can dump the file name (since this + /// object contains a file index into the support file list). + /// + /// @return + /// Returns \b true if the file and line were properly dumped, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + DumpStopContext (Stream *s, bool show_fullpaths) const; + + //------------------------------------------------------------------ + /// Check if a line entry object is valid. + /// + /// @return + /// Returns \b true if the line entry contains a valid section + /// offset address, file index, and line number, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + IsValid () const; + + //------------------------------------------------------------------ + /// Compare two LineEntry objects. + /// + /// @param[in] lhs + /// The Left Hand Side const LineEntry object reference. + /// + /// @param[in] rhs + /// The Right Hand Side const LineEntry object reference. + /// + /// @return + /// @li -1 if lhs < rhs + /// @li 0 if lhs == rhs + /// @li 1 if lhs > rhs + //------------------------------------------------------------------ + static int + Compare (const LineEntry& lhs, const LineEntry& rhs); + + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + AddressRange range; ///< The section offset address range for this line entry. + FileSpec file; + uint32_t line; ///< The source line number, or zero if there is no line number information. + uint16_t column; ///< The column number of the source line, or zero if there is no column information. + uint16_t is_start_of_statement:1, ///< Indicates this entry is the beginning of a statement. + is_start_of_basic_block:1, ///< Indicates this entry is the beginning of a basic block. + is_prologue_end:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + is_epilogue_begin:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + is_terminal_entry:1; ///< Indicates this entry is that of the first byte after the end of a sequence of target machine instructions. +}; + +//------------------------------------------------------------------ +/// Less than operator. +/// +/// @param[in] lhs +/// The Left Hand Side const LineEntry object reference. +/// +/// @param[in] rhs +/// The Right Hand Side const LineEntry object reference. +/// +/// @return +/// Returns \b true if lhs < rhs, false otherwise. +//------------------------------------------------------------------ +bool operator<(const LineEntry& lhs, const LineEntry& rhs); + +} // namespace lldb_private + +#endif // liblldb_LineEntry_h_ diff --git a/include/lldb/Symbol/LineTable.h b/include/lldb/Symbol/LineTable.h new file mode 100644 index 00000000000..477c8455ded --- /dev/null +++ b/include/lldb/Symbol/LineTable.h @@ -0,0 +1,422 @@ +//===-- LineTable.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LineTable_h_ +#define liblldb_LineTable_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/LineEntry.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/RangeMap.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class LineSequence LineTable.h "lldb/Symbol/LineTable.h" +/// @brief An abstract base class used during symbol table creation. +//---------------------------------------------------------------------- +class LineSequence +{ +public: + LineSequence (); + + virtual + ~LineSequence() {} + + virtual void + Clear() = 0; + +private: + DISALLOW_COPY_AND_ASSIGN (LineSequence); +}; + +//---------------------------------------------------------------------- +/// @class LineTable LineTable.h "lldb/Symbol/LineTable.h" +/// @brief A line table class. +//---------------------------------------------------------------------- +class LineTable +{ +public: + //------------------------------------------------------------------ + /// Construct with compile unit. + /// + /// @param[in] comp_unit + /// The compile unit to which this line table belongs. + //------------------------------------------------------------------ + LineTable (CompileUnit* comp_unit); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~LineTable (); + + //------------------------------------------------------------------ + /// Adds a new line entry to this line table. + /// + /// All line entries are maintained in file address order. + /// + /// @param[in] line_entry + /// A const reference to a new line_entry to add to this line + /// table. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ +// void +// AddLineEntry (const LineEntry& line_entry); + + // Called when you can't guarantee the addresses are in increasing order + void + 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); + + // Used to instantiate the LineSequence helper classw + LineSequence* + CreateLineSequenceContainer (); + + // Append an entry to a caller-provided collection that will later be + // inserted in this line table. + void + 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); + + // Insert a sequence of entries into this line table. + void + InsertSequence (LineSequence* sequence); + + //------------------------------------------------------------------ + /// Dump all line entries in this line table to the stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] style + /// The display style for the address. + /// + /// @see Address::DumpStyle + //------------------------------------------------------------------ + void + Dump (Stream *s, Target *target, + Address::DumpStyle style, + Address::DumpStyle fallback_style, + bool show_line_ranges); + + void + GetDescription (Stream *s, + Target *target, + lldb::DescriptionLevel level); + + //------------------------------------------------------------------ + /// Find a line entry that contains the section offset address \a + /// so_addr. + /// + /// @param[in] so_addr + /// A section offset address object containing the address we + /// are searching for. + /// + /// @param[out] line_entry + /// A copy of the line entry that was found if \b true is + /// returned, otherwise \a entry is left unmodified. + /// + /// @param[out] index_ptr + /// A pointer to a 32 bit integer that will get the actual line + /// entry index if it is not NULL. + /// + /// @return + /// Returns \b true if \a so_addr is contained in a line entry + /// in this line table, \b false otherwise. + //------------------------------------------------------------------ + bool + FindLineEntryByAddress (const Address &so_addr, LineEntry& line_entry, uint32_t *index_ptr = NULL); + + //------------------------------------------------------------------ + /// Find a line entry index that has a matching file index and + /// source line number. + /// + /// Finds the next line entry that has a matching \a file_idx and + /// source line number \a line starting at the \a start_idx entries + /// into the line entry collection. + /// + /// @param[in] start_idx + /// The number of entries to skip when starting the search. + /// + /// @param[out] file_idx + /// The file index to search for that should be found prior + /// to calling this function using the following functions: + /// CompileUnit::GetSupportFiles() + /// FileSpecList::FindFileIndex (uint32_t, const FileSpec &) const + /// + /// @param[in] line + /// The source line to match. + /// + /// @param[in] exact + /// If true, match only if you find a line entry exactly matching \a line. + /// If false, return the closest line entry greater than \a line. + /// + /// @param[out] line_entry + /// A reference to a line entry object that will get a copy of + /// the line entry if \b true is returned, otherwise \a + /// line_entry is left untouched. + /// + /// @return + /// Returns \b true if a matching line entry is found in this + /// line table, \b false otherwise. + /// + /// @see CompileUnit::GetSupportFiles() + /// @see FileSpecList::FindFileIndex (uint32_t, const FileSpec &) const + //------------------------------------------------------------------ + uint32_t + FindLineEntryIndexByFileIndex (uint32_t start_idx, + uint32_t file_idx, + uint32_t line, + bool exact, + LineEntry* line_entry_ptr); + + uint32_t + FindLineEntryIndexByFileIndex (uint32_t start_idx, + const std::vector &file_indexes, + uint32_t line, + bool exact, + LineEntry* line_entry_ptr); + + size_t + FineLineEntriesForFileIndex (uint32_t file_idx, + bool append, + SymbolContextList &sc_list); + + //------------------------------------------------------------------ + /// Get the line entry from the line table at index \a idx. + /// + /// @param[in] idx + /// An index into the line table entry collection. + /// + /// @return + /// A valid line entry if \a idx is a valid index, or an invalid + /// line entry if \a idx is not valid. + /// + /// @see LineTable::GetSize() + /// @see LineEntry::IsValid() const + //------------------------------------------------------------------ + bool + GetLineEntryAtIndex(uint32_t idx, LineEntry& line_entry); + + //------------------------------------------------------------------ + /// Gets the size of the line table in number of line table entries. + /// + /// @return + /// The number of line table entries in this line table. + //------------------------------------------------------------------ + uint32_t + GetSize () const; + + typedef lldb_private::RangeArray FileAddressRanges; + + //------------------------------------------------------------------ + /// Gets all contiguous file address ranges for the entire line table. + /// + /// @param[out] file_ranges + /// A collection of file address ranges that will be filled in + /// by this function. + /// + /// @param[out] append + /// If \b true, then append to \a file_ranges, otherwise clear + /// \a file_ranges prior to adding any ranges. + /// + /// @return + /// The number of address ranges added to \a file_ranges + //------------------------------------------------------------------ + size_t + GetContiguousFileAddressRanges (FileAddressRanges &file_ranges, bool append); + + //------------------------------------------------------------------ + /// Given a file range link map, relink the current line table + /// and return a fixed up line table. + /// + /// @param[out] file_range_map + /// A collection of file ranges that maps to new file ranges + /// that will be used when linking the line table. + /// + /// @return + /// A new line table if at least one line table entry was able + /// to be mapped. + //------------------------------------------------------------------ + typedef RangeDataVector FileRangeMap; + + LineTable * + LinkLineTable (const FileRangeMap &file_range_map); + +protected: + + struct Entry + { + Entry () : + file_addr (LLDB_INVALID_ADDRESS), + 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 (false) + { + } + + Entry ( 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) : + file_addr (_file_addr), + line (_line), + column (_column), + file_idx (_file_idx), + 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) + { + } + + int + bsearch_compare (const void *key, const void *arrmem); + + void + Clear () + { + file_addr = LLDB_INVALID_ADDRESS; + 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 = false; + } + + static int + Compare (const Entry& lhs, const Entry& rhs) + { + // Compare the sections before calling + #define SCALAR_COMPARE(a,b) if (a < b) return -1; if (a > b) return +1 + SCALAR_COMPARE (lhs.file_addr, rhs.file_addr); + SCALAR_COMPARE (lhs.line, rhs.line); + SCALAR_COMPARE (lhs.column, rhs.column); + SCALAR_COMPARE (lhs.is_start_of_statement, rhs.is_start_of_statement); + SCALAR_COMPARE (lhs.is_start_of_basic_block, rhs.is_start_of_basic_block); + // rhs and lhs reversed on purpose below. + SCALAR_COMPARE (rhs.is_prologue_end, lhs.is_prologue_end); + SCALAR_COMPARE (lhs.is_epilogue_begin, rhs.is_epilogue_begin); + // rhs and lhs reversed on purpose below. + SCALAR_COMPARE (rhs.is_terminal_entry, lhs.is_terminal_entry); + SCALAR_COMPARE (lhs.file_idx, rhs.file_idx); + #undef SCALAR_COMPARE + return 0; + } + + + class LessThanBinaryPredicate + { + public: + LessThanBinaryPredicate(LineTable *line_table); + bool operator() (const LineTable::Entry&, const LineTable::Entry&) const; + protected: + LineTable *m_line_table; + }; + + static bool EntryAddressLessThan (const Entry& lhs, const Entry& rhs) + { + return lhs.file_addr < rhs.file_addr; + } + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + lldb::addr_t file_addr; ///< The file address for this line entry + uint32_t line; ///< The source line number, or zero if there is no line number information. + uint16_t column; ///< The column number of the source line, or zero if there is no column information. + uint16_t file_idx:11, ///< The file index into CompileUnit's file table, or zero if there is no file information. + is_start_of_statement:1, ///< Indicates this entry is the beginning of a statement. + is_start_of_basic_block:1, ///< Indicates this entry is the beginning of a basic block. + is_prologue_end:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an entry breakpoint of a function. + is_epilogue_begin:1, ///< Indicates this entry is one (of possibly many) where execution should be suspended for an exit breakpoint of a function. + is_terminal_entry:1; ///< Indicates this entry is that of the first byte after the end of a sequence of target machine instructions. + }; + + struct EntrySearchInfo + { + LineTable* line_table; + lldb_private::Section *a_section; + Entry *a_entry; + }; + + //------------------------------------------------------------------ + // Types + //------------------------------------------------------------------ + typedef std::vector section_collection; ///< The collection type for the sections. + typedef std::vector entry_collection; ///< The collection type for the line entries. + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + CompileUnit* m_comp_unit; ///< The compile unit that this line table belongs to. + entry_collection m_entries; ///< The collection of line entries in this line table. + + //------------------------------------------------------------------ + // Helper class + //------------------------------------------------------------------ + class LineSequenceImpl : public LineSequence + { + public: + LineSequenceImpl() : + LineSequence() + {} + + virtual + ~LineSequenceImpl() + {} + + virtual void + Clear(); + + entry_collection m_entries; ///< The collection of line entries in this sequence. + }; + + bool + ConvertEntryAtIndexToLineEntry (uint32_t idx, LineEntry &line_entry); + +private: + DISALLOW_COPY_AND_ASSIGN (LineTable); +}; + +} // namespace lldb_private + +#endif // liblldb_LineTable_h_ diff --git a/include/lldb/Symbol/ObjectContainer.h b/include/lldb/Symbol/ObjectContainer.h new file mode 100644 index 00000000000..7fb68624505 --- /dev/null +++ b/include/lldb/Symbol/ObjectContainer.h @@ -0,0 +1,236 @@ +//===-- ObjectContainer.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectContainer_h_ +#define liblldb_ObjectContainer_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Host/Endian.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ObjectContainer ObjectContainer.h "lldb/Symbol/ObjectContainer.h" +/// @brief A plug-in interface definition class for object containers. +/// +/// Object containers contain object files from one or more +/// architectures, and also can contain one or more named objects. +/// +/// Typical object containers are static libraries (.a files) that +/// contain multiple named object files, and universal files that contain +/// multiple architectures. +//---------------------------------------------------------------------- +class ObjectContainer : + public PluginInterface, + public ModuleChild +{ +public: + //------------------------------------------------------------------ + /// Construct with a parent module, offset, and header data. + /// + /// Object files belong to modules and a valid module must be + /// supplied upon construction. The at an offset within a file for + /// objects that contain more than one architecture or object. + //------------------------------------------------------------------ + ObjectContainer (const lldb::ModuleSP &module_sp, + const FileSpec *file, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset) : + ModuleChild (module_sp), + m_file (), // This file can be different than the module's file spec + m_offset (file_offset), + m_length (length), + m_data () + { + if (file) + m_file = *file; + if (data_sp) + m_data.SetData (data_sp, data_offset, length); + } + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~ObjectContainer() + { + } + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the current contents of this object + /// to the supplied stream \a s. The dumping should include the + /// section list if it has been parsed, and the symbol table + /// if it has been parsed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) const = 0; + + //------------------------------------------------------------------ + /// Gets the architecture given an index. + /// + /// Copies the architecture specification for index \a idx. + /// + /// @param[in] idx + /// The architecture index to extract. + /// + /// @param[out] arch + /// A architecture object that will be filled in if \a idx is a + /// architecture valid index. + /// + /// @return + /// Returns \b true if \a idx is valid and \a arch has been + /// filled in, \b false otherwise. + /// + /// @see ObjectContainer::GetNumArchitectures() const + //------------------------------------------------------------------ + virtual bool + GetArchitectureAtIndex (uint32_t idx, ArchSpec& arch) const + { + return false; + } + + //------------------------------------------------------------------ + /// Returns the offset into a file at which this object resides. + /// + /// Some files contain many object files, and this function allows + /// access to an object's offset within the file. + /// + /// @return + /// The offset in bytes into the file. Defaults to zero for + /// simple object files that a represented by an entire file. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetOffset () const + { return m_offset; } + + virtual lldb::addr_t + GetByteSize () const + { return m_length; } + + //------------------------------------------------------------------ + /// Get the number of objects within this object file (archives). + /// + /// @return + /// Zero for object files that are not archives, or the number + /// of objects contained in the archive. + //------------------------------------------------------------------ + virtual size_t + GetNumObjects () const + { return 0; } + + //------------------------------------------------------------------ + /// Get the number of architectures in this object file. + /// + /// The default implementation returns 1 as for object files that + /// contain a single architecture. ObjectContainer instances that + /// contain more than one architecture should override this function + /// and return an appropriate value. + /// + /// @return + /// The number of architectures contained in this object file. + //------------------------------------------------------------------ + virtual size_t + GetNumArchitectures () const + { return 0; } + + //------------------------------------------------------------------ + /// Attempts to parse the object header. + /// + /// This function is used as a test to see if a given plug-in + /// instance can parse the header data already contained in + /// ObjectContainer::m_data. If an object file parser does not + /// recognize that magic bytes in a header, false should be returned + /// and the next plug-in can attempt to parse an object file. + /// + /// @return + /// Returns \b true if the header was parsed succesfully, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + ParseHeader () = 0; + + //------------------------------------------------------------------ + /// Selects an architecture in an object file. + /// + /// Object files that contain a single architecture should verify + /// that the specified \a arch matches the architecture in in + /// object file and return \b true or \b false accordingly. + /// + /// Object files that contain more than one architecture should + /// attempt to select that architecture, and if successful, clear + /// out any previous state from any previously selected architecture + /// and prepare to return information for the new architecture. + /// + /// @return + /// Returns a pointer to the object file of the requested \a + /// arch and optional \a name. Returns NULL of no such object + /// file exists in the container. + //------------------------------------------------------------------ + virtual lldb::ObjectFileSP + GetObjectFile (const FileSpec *file) = 0; + + virtual bool + ObjectAtIndexIsContainer (uint32_t object_idx) + { + return false; + } + + virtual ObjectFile * + GetObjectFileAtIndex (uint32_t object_idx) + { + return NULL; + } + + virtual ObjectContainer * + GetObjectContainerAtIndex (uint32_t object_idx) + { + return NULL; + } + + virtual const char * + GetObjectNameAtIndex (uint32_t object_idx) const + { + return NULL; + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + FileSpec m_file; ///< The file that represents this container objects (which can be different from the module's file). + lldb::addr_t m_offset; ///< The offset in bytes into the file, or the address in memory + lldb::addr_t m_length; ///< The size in bytes if known (can be zero). + DataExtractor m_data; ///< The data for this object file so things can be parsed lazily. + +private: + DISALLOW_COPY_AND_ASSIGN (ObjectContainer); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjectContainer_h_ diff --git a/include/lldb/Symbol/ObjectFile.h b/include/lldb/Symbol/ObjectFile.h new file mode 100644 index 00000000000..8934c31bb98 --- /dev/null +++ b/include/lldb/Symbol/ObjectFile.h @@ -0,0 +1,706 @@ +//===-- ObjectFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFile_h_ +#define liblldb_ObjectFile_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/UnwindTable.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ObjectFile ObjectFile.h "lldb/Symbol/ObjectFile.h" +/// @brief A plug-in interface definition class for object file parsers. +/// +/// Object files belong to Module objects and know how to extract +/// information from executable, shared library, and object (.o) files +/// used by operating system runtime. The symbol table and section list +/// for an object file. +/// +/// Object files can be represented by the entire file, or by part of a +/// file. Examples of object files that are part of a file include +/// object files that contain information for multiple architectures in +/// the same file, or archive files that contain multiple objects +/// (ranlib archives) (possibly for multiple architectures as well). +/// +/// Object archive files (e.g. ranlib archives) can contain +/// multiple .o (object) files that must be selected by index or by name. +/// The number of objects that an ObjectFile contains can be determined +/// using the ObjectFile::GetNumObjects() const +/// function, and followed by a call to +/// ObjectFile::SelectObjectAtIndex (uint32_t) to change the currently +/// selected object. Objects can also be selected by name using the +/// ObjectFile::SelectObject(const char *) function. +/// +/// Once an architecture is selected (and an object is selected for +/// for archives), the object file information can be extracted from +/// this abstract class. +//---------------------------------------------------------------------- +class ObjectFile: + public std::enable_shared_from_this, + public PluginInterface, + public ModuleChild +{ +friend class lldb_private::Module; + +public: + typedef enum + { + eTypeInvalid = 0, + eTypeCoreFile, /// A core file that has a checkpoint of a program's execution state + eTypeExecutable, /// A normal executable + eTypeDebugInfo, /// An object file that contains only debug information + eTypeDynamicLinker, /// The platform's dynamic linker executable + eTypeObjectFile, /// An intermediate object file + eTypeSharedLibrary, /// A shared library that can be used during execution + eTypeStubLibrary, /// A library that can be linked against but not used for execution + eTypeUnknown + } Type; + + typedef enum + { + eStrataInvalid = 0, + eStrataUnknown, + eStrataUser, + eStrataKernel, + eStrataRawImage + } Strata; + + //------------------------------------------------------------------ + /// Construct with a parent module, offset, and header data. + /// + /// Object files belong to modules and a valid module must be + /// supplied upon construction. The at an offset within a file for + /// objects that contain more than one architecture or object. + //------------------------------------------------------------------ + ObjectFile (const lldb::ModuleSP &module_sp, + const FileSpec *file_spec_ptr, + lldb::offset_t file_offset, + lldb::offset_t length, + lldb::DataBufferSP& data_sp, + lldb::offset_t data_offset); + + ObjectFile (const lldb::ModuleSP &module_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr, + lldb::DataBufferSP& data_sp); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~ObjectFile(); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the current contents of this object + /// to the supplied stream \a s. The dumping should include the + /// section list if it has been parsed, and the symbol table + /// if it has been parsed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + virtual void + Dump (Stream *s) = 0; + + //------------------------------------------------------------------ + /// Find a ObjectFile plug-in that can parse \a file_spec. + /// + /// Scans all loaded plug-in interfaces that implement versions of + /// the ObjectFile plug-in interface and returns the first + /// instance that can parse the file. + /// + /// @param[in] module + /// The parent module that owns this object file. + /// + /// @param[in] file_spec + /// A file specification that indicates which file to use as the + /// object file. + /// + /// @param[in] file_offset + /// The offset into the file at which to start parsing the + /// object. This is for files that contain multiple + /// architectures or objects. + /// + /// @param[in] file_size + /// The size of the current object file if it can be determined + /// or if it is known. This can be zero. + /// + /// @see ObjectFile::ParseHeader() + //------------------------------------------------------------------ + static lldb::ObjectFileSP + FindPlugin (const lldb::ModuleSP &module_sp, + const FileSpec* file_spec, + lldb::offset_t file_offset, + lldb::offset_t file_size, + lldb::DataBufferSP &data_sp, + lldb::offset_t &data_offset); + + //------------------------------------------------------------------ + /// Find a ObjectFile plug-in that can parse a file in memory. + /// + /// Scans all loaded plug-in interfaces that implement versions of + /// the ObjectFile plug-in interface and returns the first + /// instance that can parse the file. + /// + /// @param[in] module + /// The parent module that owns this object file. + /// + /// @param[in] process_sp + /// A shared pointer to the process whose memory space contains + /// an object file. This will be stored as a std::weak_ptr. + /// + /// @param[in] header_addr + /// The address of the header for the object file in memory. + //------------------------------------------------------------------ + static lldb::ObjectFileSP + FindPlugin (const lldb::ModuleSP &module_sp, + const lldb::ProcessSP &process_sp, + lldb::addr_t header_addr, + lldb::DataBufferSP &file_data_sp); + + + static size_t + GetModuleSpecifications (const FileSpec &file, + lldb::offset_t file_offset, + lldb::offset_t file_size, + ModuleSpecList &specs); + + 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 file_size, + lldb_private::ModuleSpecList &specs); + //------------------------------------------------------------------ + /// Split a path into a file path with object name. + /// + /// For paths like "/tmp/foo.a(bar.o)" we often need to split a path + /// up into the actual path name and into the object name so we can + /// make a valid object file from it. + /// + /// @param[in] path_with_object + /// A path that might contain an archive path with a .o file + /// specified in parens in the basename of the path. + /// + /// @param[out] archive_file + /// If \b true is returned, \a file_spec will be filled in with + /// the path to the archive. + /// + /// @param[out] archive_object + /// If \b true is returned, \a object will be filled in with + /// the name of the object inside the archive. + /// + /// @return + /// \b true if the path matches the pattern of archive + object + /// and \a archive_file and \a archive_object are modified, + /// \b false otherwise and \a archive_file and \a archive_object + /// are guaranteed to be remain unchanged. + //------------------------------------------------------------------ + static bool + SplitArchivePathWithObject (const char *path_with_object, + lldb_private::FileSpec &archive_file, + lldb_private::ConstString &archive_object, + bool must_exist); + + //------------------------------------------------------------------ + /// Gets the address size in bytes for the current object file. + /// + /// @return + /// The size of an address in bytes for the currently selected + /// architecture (and object for archives). Returns zero if no + /// architecture or object has been selected. + //------------------------------------------------------------------ + virtual uint32_t + GetAddressByteSize () const = 0; + + //------------------------------------------------------------------ + /// Get the address type given a file address in an object file. + /// + /// Many binary file formats know what kinds + /// This is primarily for ARM binaries, though it can be applied to + /// any executable file format that supports different opcode types + /// within the same binary. ARM binaries support having both ARM and + /// Thumb within the same executable container. We need to be able + /// to get + /// @return + /// The size of an address in bytes for the currently selected + /// architecture (and object for archives). Returns zero if no + /// architecture or object has been selected. + //------------------------------------------------------------------ + virtual lldb::AddressClass + GetAddressClass (lldb::addr_t file_addr); + + //------------------------------------------------------------------ + /// Extract the dependent modules from an object file. + /// + /// If an object file has information about which other images it + /// depends on (such as shared libraries), this function will + /// provide the list. Since many executables or shared libraries + /// may depend on the same files, + /// FileSpecList::AppendIfUnique(const FileSpec &) should be + /// used to make sure any files that are added are not already in + /// the list. + /// + /// @param[out] file_list + /// A list of file specification objects that gets dependent + /// files appended to. + /// + /// @return + /// The number of new files that were appended to \a file_list. + /// + /// @see FileSpecList::AppendIfUnique(const FileSpec &) + //------------------------------------------------------------------ + virtual uint32_t + GetDependentModules (FileSpecList& file_list) = 0; + + //------------------------------------------------------------------ + /// Tells whether this object file is capable of being the main executable + /// for a process. + /// + /// @return + /// \b true if it is, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + IsExecutable () const = 0; + + //------------------------------------------------------------------ + /// Returns the offset into a file at which this object resides. + /// + /// Some files contain many object files, and this function allows + /// access to an object's offset within the file. + /// + /// @return + /// The offset in bytes into the file. Defaults to zero for + /// simple object files that a represented by an entire file. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetFileOffset () const + { return m_file_offset; } + + virtual lldb::addr_t + GetByteSize () const + { return m_length; } + + //------------------------------------------------------------------ + /// Get accessor to the object file specification. + /// + /// @return + /// The file specification object pointer if there is one, or + /// NULL if this object is only from memory. + //------------------------------------------------------------------ + virtual FileSpec& + GetFileSpec() { return m_file; } + + //------------------------------------------------------------------ + /// Get const accessor to the object file specification. + /// + /// @return + /// The const file specification object pointer if there is one, + /// or NULL if this object is only from memory. + //------------------------------------------------------------------ + virtual const FileSpec& + GetFileSpec() const { return m_file; } + + //------------------------------------------------------------------ + /// Get the name of the cpu, vendor and OS for this object file. + /// + /// This value is a string that represents the target triple where + /// the cpu type, the vendor and the OS are encoded into a string. + /// + /// @param[out] target_triple + /// The string value of the target triple. + /// + /// @return + /// \b True if the target triple was able to be computed, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + GetArchitecture (ArchSpec &arch) = 0; + + //------------------------------------------------------------------ + /// Gets the section list for the currently selected architecture + /// (and object for archives). + /// + /// Section list parsing can be deferred by ObjectFile instances + /// until this accessor is called the first time. + /// + /// @return + /// The list of sections contained in this object file. + //------------------------------------------------------------------ + virtual SectionList * + GetSectionList (); + + virtual void + CreateSections (SectionList &unified_section_list) = 0; + + //------------------------------------------------------------------ + /// Gets the symbol table for the currently selected architecture + /// (and object for archives). + /// + /// Symbol table parsing can be deferred by ObjectFile instances + /// until this accessor is called the first time. + /// + /// @return + /// The symbol table for this object file. + //------------------------------------------------------------------ + virtual Symtab * + GetSymtab () = 0; + + //------------------------------------------------------------------ + /// Detect if this object file has been stripped of local symbols. + /// + /// @return + /// Return \b true if the object file has been stripped of local + /// symbols. + //------------------------------------------------------------------ + virtual bool + IsStripped () = 0; + + //------------------------------------------------------------------ + /// Frees the symbol table. + /// + /// This function should only be used when an object file is + /// + /// @param[in] flags + /// eSymtabFromUnifiedSectionList: Whether to clear symbol table + /// for unified module section list, or object file. + /// + /// @return + /// The symbol table for this object file. + //------------------------------------------------------------------ + virtual void + ClearSymtab (); + + //------------------------------------------------------------------ + /// Gets the UUID for this object file. + /// + /// If the object file format contains a UUID, the value should be + /// returned. Else ObjectFile instances should return the MD5 + /// checksum of all of the bytes for the object file (or memory for + /// memory based object files). + /// + /// @return + /// Returns \b true if a UUID was successfully extracted into + /// \a uuid, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + GetUUID (lldb_private::UUID* uuid) = 0; + + //------------------------------------------------------------------ + /// Gets the symbol file spec list for this object file. + /// + /// If the object file format contains a debug symbol file link, + /// the values will be return in the FileSpecList. + /// + /// @return + /// Returns filespeclist. + //------------------------------------------------------------------ + virtual lldb_private::FileSpecList + GetDebugSymbolFilePaths() + { + return FileSpecList(); + } + + //------------------------------------------------------------------ + /// Gets whether endian swapping should occur when extracting data + /// from this object file. + /// + /// @return + /// Returns \b true if endian swapping is needed, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual lldb::ByteOrder + GetByteOrder () const = 0; + + //------------------------------------------------------------------ + /// Attempts to parse the object header. + /// + /// This function is used as a test to see if a given plug-in + /// instance can parse the header data already contained in + /// ObjectFile::m_data. If an object file parser does not + /// recognize that magic bytes in a header, false should be returned + /// and the next plug-in can attempt to parse an object file. + /// + /// @return + /// Returns \b true if the header was parsed succesfully, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual bool + ParseHeader () = 0; + + //------------------------------------------------------------------ + /// Returns a reference to the UnwindTable for this ObjectFile + /// + /// The UnwindTable contains FuncUnwinders objects for any function in + /// this ObjectFile. If a FuncUnwinders object hasn't been created yet + /// (i.e. the function has yet to be unwound in a stack walk), it + /// will be created when requested. Specifically, we do not create + /// FuncUnwinders objects for functions until they are needed. + /// + /// @return + /// Returns the unwind table for this object file. + //------------------------------------------------------------------ + virtual lldb_private::UnwindTable& + GetUnwindTable () { return m_unwind_table; } + + //------------------------------------------------------------------ + /// Similar to Process::GetImageInfoAddress(). + /// + /// Some platforms embed auxiliary structures useful to debuggers in the + /// address space of the inferior process. This method returns the address + /// of such a structure if the information can be resolved via entries in + /// the object file. ELF, for example, provides a means to hook into the + /// runtime linker so that a debugger may monitor the loading and unloading + /// of shared libraries. + /// + /// @return + /// The address of any auxiliary tables, or an invalid address if this + /// object file format does not support or contain such information. + virtual lldb_private::Address + GetImageInfoAddress () { return Address(); } + + //------------------------------------------------------------------ + /// Returns the address of the Entry Point in this object file - if + /// the object file doesn't have an entry point (because it is not an + /// executable file) then an invalid address is returned. + /// + /// @return + /// Returns the entry address for this module. + //------------------------------------------------------------------ + virtual lldb_private::Address + GetEntryPointAddress () { return Address();} + + //------------------------------------------------------------------ + /// Returns the address that represents the header of this object + /// file. + /// + /// The header address is defined as where the header for the object + /// file is that describes the content of the file. If the header + /// doesn't appear in a section that is defined in the object file, + /// an address with no section is returned that has the file offset + /// set in the m_file_offset member of the lldb_private::Address object. + /// + /// @return + /// Returns the entry address for this module. + //------------------------------------------------------------------ + virtual lldb_private::Address + GetHeaderAddress () { return Address(m_memory_addr);} + + + virtual uint32_t + GetNumThreadContexts () + { + return 0; + } + + virtual lldb::RegisterContextSP + GetThreadContextAtIndex (uint32_t idx, lldb_private::Thread &thread) + { + return lldb::RegisterContextSP(); + } + + //------------------------------------------------------------------ + /// The object file should be able to calculate its type by looking + /// at its file header and possibly the sections or other data in + /// the object file. The file type is used in the debugger to help + /// select the correct plug-ins for the job at hand, so this is + /// important to get right. If any eTypeXXX definitions do not match + /// up with the type of file you are loading, please feel free to + /// add a new enumeration value. + /// + /// @return + /// The calculated file type for the current object file. + //------------------------------------------------------------------ + virtual Type + CalculateType() = 0; + + //------------------------------------------------------------------ + /// In cases where the type can't be calculated (elf files), this + /// routine allows someone to explicitly set it. As an example, + /// SymbolVendorELF uses this routine to set eTypeDebugInfo when + /// loading debug link files. + virtual void + SetType (Type type) + { + m_type = type; + } + + //------------------------------------------------------------------ + /// The object file should be able to calculate the strata of the + /// object file. + /// + /// Many object files for platforms might be for either user space + /// debugging or for kernel debugging. If your object file subclass + /// can figure this out, it will help with debugger plug-in selection + /// when it comes time to debug. + /// + /// @return + /// The calculated object file strata for the current object + /// file. + //------------------------------------------------------------------ + virtual Strata + CalculateStrata() = 0; + + //------------------------------------------------------------------ + /// Get the object file version numbers. + /// + /// Many object files have a set of version numbers that describe + /// the version of the executable or shared library. Typically there + /// are major, minor and build, but there may be more. This function + /// will extract the versions from object files if they are available. + /// + /// If \a versions is NULL, or if \a num_versions is 0, the return + /// value will indicate how many version numbers are available in + /// this object file. Then a subsequent call can be made to this + /// function with a value of \a versions and \a num_versions that + /// has enough storage to store some or all version numbers. + /// + /// @param[out] versions + /// A pointer to an array of uint32_t types that is \a num_versions + /// long. If this value is NULL, the return value will indicate + /// how many version numbers are required for a subsequent call + /// to this function so that all versions can be retrieved. If + /// the value is non-NULL, then at most \a num_versions of the + /// existing versions numbers will be filled into \a versions. + /// If there is no version information available, \a versions + /// will be filled with \a num_versions UINT32_MAX values + /// and zero will be returned. + /// + /// @param[in] num_versions + /// The maximum number of entries to fill into \a versions. If + /// this value is zero, then the return value will indicate + /// how many version numbers there are in total so another call + /// to this function can be make with adequate storage in + /// \a versions to get all of the version numbers. If \a + /// num_versions is less than the actual number of version + /// numbers in this object file, only \a num_versions will be + /// filled into \a versions (if \a versions is non-NULL). + /// + /// @return + /// This function always returns the number of version numbers + /// that this object file has regardless of the number of + /// version numbers that were copied into \a versions. + //------------------------------------------------------------------ + virtual uint32_t + GetVersion (uint32_t *versions, uint32_t num_versions) + { + if (versions && num_versions) + { + for (uint32_t i=0; i m_sections_ap; + std::unique_ptr m_symtab_ap; + + //------------------------------------------------------------------ + /// Sets the architecture for a module. At present the architecture + /// can only be set if it is invalid. It is not allowed to switch from + /// one concrete architecture to another. + /// + /// @param[in] new_arch + /// The architecture this module will be set to. + /// + /// @return + /// Returns \b true if the architecture was changed, \b + /// false otherwise. + //------------------------------------------------------------------ + bool SetModulesArchitecture (const ArchSpec &new_arch); + +private: + DISALLOW_COPY_AND_ASSIGN (ObjectFile); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjectFile_h_ + diff --git a/include/lldb/Symbol/Symbol.h b/include/lldb/Symbol/Symbol.h new file mode 100644 index 00000000000..11c1cc7af9a --- /dev/null +++ b/include/lldb/Symbol/Symbol.h @@ -0,0 +1,317 @@ +//===-- Symbol.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Symbol_h_ +#define liblldb_Symbol_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/UserID.h" +#include "lldb/Symbol/SymbolContextScope.h" + +namespace lldb_private { + +class Symbol : + public SymbolContextScope +{ +public: + // ObjectFile readers can classify their symbol table entries and searches can be made + // on specific types where the symbol values will have drastically different meanings + // and sorting requirements. + Symbol(); + + Symbol (uint32_t symID, + const char *name, + bool name_is_mangled, + lldb::SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const lldb::SectionSP §ion_sp, + lldb::addr_t value, + lldb::addr_t size, + bool size_is_valid, + uint32_t flags); + + Symbol (uint32_t symID, + const char *name, + bool name_is_mangled, + lldb::SymbolType type, + bool external, + bool is_debug, + bool is_trampoline, + bool is_artificial, + const AddressRange &range, + bool size_is_valid, + uint32_t flags); + + Symbol (const Symbol& rhs); + + const Symbol& + operator= (const Symbol& rhs); + + void + Clear(); + + bool + Compare (const ConstString& name, lldb::SymbolType type) const; + + void + Dump (Stream *s, Target *target, uint32_t index) const; + + bool + ValueIsAddress() const; + + //------------------------------------------------------------------ + // Access the address value. Do NOT hand out the AddressRange as an + // object as the byte size of the address range may not be filled in + // and it should be accessed via GetByteSize(). + //------------------------------------------------------------------ + Address & + GetAddress() + { + return m_addr_range.GetBaseAddress(); + } + + //------------------------------------------------------------------ + // Access the address value. Do NOT hand out the AddressRange as an + // object as the byte size of the address range may not be filled in + // and it should be accessed via GetByteSize(). + //------------------------------------------------------------------ + const Address & + GetAddress() const + { + return m_addr_range.GetBaseAddress(); + } + + const ConstString & + GetName () const + { + return m_mangled.GetName(); + } + + uint32_t + GetID() const + { + return m_uid; + } + + void + SetID(uint32_t uid) + { + m_uid = uid; + } + + Mangled& + GetMangled () + { + return m_mangled; + } + + const Mangled& + GetMangled () const + { + return m_mangled; + } + + uint32_t + GetSiblingIndex () const; + + lldb::SymbolType + GetType () const + { + return (lldb::SymbolType)m_type; + } + + void + SetType (lldb::SymbolType type) + { + m_type = (lldb::SymbolType)type; + } + + const char * + GetTypeAsString () const; + + uint32_t + GetFlags () const + { + return m_flags; + } + + void + SetFlags (uint32_t flags) + { + m_flags = flags; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level, Target *target) const; + + bool + IsSynthetic () const + { + return m_is_synthetic; + } + + void + SetIsSynthetic (bool b) + { + m_is_synthetic = b; + } + + + bool + GetSizeIsSynthesized() const + { + return m_size_is_synthesized; + } + + void + SetSizeIsSynthesized(bool b) + { + m_size_is_synthesized = b; + } + + bool + IsDebug () const + { + return m_is_debug; + } + + void + SetDebug (bool b) + { + m_is_debug = b; + } + + bool + IsExternal () const + { + return m_is_external; + } + + void + SetExternal (bool b) + { + m_is_external = b; + } + + bool + IsTrampoline () const; + + bool + IsIndirect () const; + + bool + GetByteSizeIsValid () const + { + return m_size_is_valid; + } + + lldb::addr_t + GetByteSize () const; + + void + SetByteSize (lldb::addr_t size) + { + m_size_is_valid = size > 0; + m_addr_range.SetByteSize(size); + } + + bool + GetSizeIsSibling () const + { + return m_size_is_sibling; + } + + void + SetSizeIsSibling (bool b) + { + m_size_is_sibling = b; + } + +// void +// SetValue (Address &value) +// { +// m_addr_range.GetBaseAddress() = value; +// } +// +// void +// SetValue (const AddressRange &range) +// { +// m_addr_range = range; +// } +// +// void +// SetValue (lldb::addr_t value); +// { +// m_addr_range.GetBaseAddress().SetRawAddress(value); +// } + + // If m_type is "Code" or "Function" then this will return the prologue size + // in bytes, else it will return zero. + uint32_t + GetPrologueByteSize (); + + bool + GetDemangledNameIsSynthesized() const + { + return m_demangled_is_synthesized; + } + void + SetDemangledNameIsSynthesized(bool b) + { + m_demangled_is_synthesized = b; + } + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::CalculateSymbolContext(SymbolContext*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext *sc); + + virtual lldb::ModuleSP + CalculateSymbolContextModule (); + + virtual Symbol * + CalculateSymbolContextSymbol (); + + //------------------------------------------------------------------ + /// @copydoc SymbolContextScope::DumpSymbolContext(Stream*) + /// + /// @see SymbolContextScope + //------------------------------------------------------------------ + virtual void + DumpSymbolContext (Stream *s); + +protected: + + uint32_t m_uid; // User ID (usually the original symbol table index) + uint16_t m_type_data; // data specific to m_type + uint16_t m_type_data_resolved:1, // True if the data in m_type_data has already been calculated + m_is_synthetic:1, // non-zero if this symbol is not actually in the symbol table, but synthesized from other info in the object file. + m_is_debug:1, // non-zero if this symbol is debug information in a symbol + m_is_external:1, // non-zero if this symbol is globally visible + m_size_is_sibling:1, // m_size contains the index of this symbol's sibling + m_size_is_synthesized:1,// non-zero if this symbol's size was calculated using a delta between this symbol and the next + m_size_is_valid:1, + m_demangled_is_synthesized:1, // The demangled name was created should not be used for expressions or other lookups + m_type:8; + Mangled m_mangled; // uniqued symbol name/mangled name pair + AddressRange m_addr_range; // Contains the value, or the section offset address when the value is an address in a section, and the size (if any) + uint32_t m_flags; // A copy of the flags from the original symbol table, the ObjectFile plug-in can interpret these +}; + +} // namespace lldb_private + +#endif // liblldb_Symbol_h_ diff --git a/include/lldb/Symbol/SymbolContext.h b/include/lldb/Symbol/SymbolContext.h new file mode 100644 index 00000000000..5b12adebf5f --- /dev/null +++ b/include/lldb/Symbol/SymbolContext.h @@ -0,0 +1,566 @@ +//===-- SymbolContext.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_SymbolContext_h_ +#define liblldb_SymbolContext_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/LineEntry.h" + +namespace lldb_private { + +class SymbolContextScope; +//---------------------------------------------------------------------- +/// @class SymbolContext SymbolContext.h "lldb/Symbol/SymbolContext.h" +/// @brief Defines a symbol context baton that can be handed other debug +/// core functions. +/// +/// Many debugger functions require a context when doing lookups. This +/// class provides a common structure that can be used as the result +/// of a query that can contain a single result. Examples of such +/// queries include +/// @li Looking up a load address. +//---------------------------------------------------------------------- +class SymbolContext +{ +public: + + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize all pointer members to NULL and all struct members + /// to their default state. + //------------------------------------------------------------------ + SymbolContext (); + + //------------------------------------------------------------------ + /// Construct with an object that knows how to reconstruct its + /// symbol context. + /// + /// @param[in] sc_scope + /// A symbol context scope object that knows how to reconstruct + /// it's context. + //------------------------------------------------------------------ + explicit + SymbolContext (SymbolContextScope *sc_scope); + + //------------------------------------------------------------------ + /// Construct with module, and optional compile unit, function, + /// block, line table, line entry and symbol. + /// + /// Initialize all pointer to the specified values. + /// + /// @param[in] module + /// A Module pointer to the module for this context. + /// + /// @param[in] comp_unit + /// A CompileUnit pointer to the compile unit for this context. + /// + /// @param[in] function + /// A Function pointer to the function for this context. + /// + /// @param[in] block + /// A Block pointer to the deepest block for this context. + /// + /// @param[in] line_entry + /// A LineEntry pointer to the line entry for this context. + /// + /// @param[in] symbol + /// A Symbol pointer to the symbol for this context. + //------------------------------------------------------------------ + explicit + SymbolContext (const lldb::TargetSP &target_sp, + const lldb::ModuleSP &module_sp, + CompileUnit *comp_unit = NULL, + Function *function = NULL, + Block *block = NULL, + LineEntry *line_entry = NULL, + Symbol *symbol = NULL); + + // This version sets the target to a NULL TargetSP if you don't know it. + explicit + SymbolContext (const lldb::ModuleSP &module_sp, + CompileUnit *comp_unit = NULL, + Function *function = NULL, + Block *block = NULL, + LineEntry *line_entry = NULL, + Symbol *symbol = NULL); + + ~SymbolContext (); + //------------------------------------------------------------------ + /// Copy constructor + /// + /// Makes a copy of the another SymbolContext object \a rhs. + /// + /// @param[in] rhs + /// A const SymbolContext object reference to copy. + //------------------------------------------------------------------ + SymbolContext (const SymbolContext& rhs); + + //------------------------------------------------------------------ + /// Assignment operator. + /// + /// Copies the address value from another SymbolContext object \a + /// rhs into \a this object. + /// + /// @param[in] rhs + /// A const SymbolContext object reference to copy. + /// + /// @return + /// A const SymbolContext object reference to \a this. + //------------------------------------------------------------------ + const SymbolContext& + operator= (const SymbolContext& rhs); + + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Resets all pointer members to NULL, and clears any class objects + /// to their default state. + //------------------------------------------------------------------ + void + Clear (bool clear_target); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, Target *target) const; + + //------------------------------------------------------------------ + /// Dump the stop context in this object to a Stream. + /// + /// Dump the best description of this object to the stream. The + /// information displayed depends on the amount and quality of the + /// information in this context. If a module, function, file and + /// line number are available, they will be dumped. If only a + /// module and function or symbol name with offset is available, + /// that will be output. Else just the address at which the target + /// was stopped will be displayed. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + /// + /// @param[in] so_addr + /// The resolved section offset address. + //------------------------------------------------------------------ + bool + DumpStopContext (Stream *s, + ExecutionContextScope *exe_scope, + const Address &so_addr, + bool show_fullpaths, + bool show_module, + bool show_inlined_frames) const; + + //------------------------------------------------------------------ + /// Get the address range contained within a symbol context. + /// + /// Address range priority is as follows: + /// - line_entry address range if line_entry is valid and eSymbolContextLineEntry is set in \a scope + /// - block address range if block is not NULL and eSymbolContextBlock is set in \a scope + /// - function address range if function is not NULL and eSymbolContextFunction is set in \a scope + /// - symbol address range if symbol is not NULL and eSymbolContextSymbol is set in \a scope + /// + /// @param[in] scope + /// A mask of symbol context bits telling this function which + /// address ranges it can use when trying to extract one from + /// the valid (non-NULL) symbol context classes. + /// + /// @param[in] range_idx + /// The address range index to grab. Since many functions and + /// blocks are not always contiguous, they may have more than + /// one address range. + /// + /// @param[in] use_inline_block_range + /// If \a scope has the eSymbolContextBlock bit set, and there + /// is a valid block in the symbol context, return the block + /// address range for the containing inline function block, not + /// the deepest most block. This allows us to extract information + /// for the address range of the inlined function block, not + /// the deepest lexical block. + /// + /// @param[out] range + /// An address range object that will be filled in if \b true + /// is returned. + /// + /// @return + /// \b True if this symbol context contains items that describe + /// an address range, \b false otherwise. + //------------------------------------------------------------------ + bool + GetAddressRange (uint32_t scope, + uint32_t range_idx, + bool use_inline_block_range, + AddressRange &range) const; + + + void + GetDescription(Stream *s, + lldb::DescriptionLevel level, + Target *target) const; + + uint32_t + GetResolvedMask () const; + + + //------------------------------------------------------------------ + /// Find a block that defines the function represented by this + /// symbol context. + /// + /// If this symbol context points to a block that is an inlined + /// function, or is contained within an inlined function, the block + /// that defines the inlined function is returned. + /// + /// If this symbol context has no block in it, or the block is not + /// itself an inlined function block or contained within one, we + /// return the top level function block. + /// + /// This is a handy function to call when you want to get the block + /// whose variable list will include the arguments for the function + /// that is represented by this symbol context (whether the function + /// is an inline function or not). + /// + /// @return + /// The block object pointer that defines the function that is + /// represented by this symbol context object, NULL otherwise. + //------------------------------------------------------------------ + Block * + GetFunctionBlock (); + + + //------------------------------------------------------------------ + /// If this symbol context represents a function that is a method, + /// return true and provide information about the method. + /// + /// @param[out] language + /// If \b true is returned, the language for the method. + /// + /// @param[out] is_instance_method + /// If \b true is returned, \b true if this is a instance method, + /// \b false if this is a static/class function. + /// + /// @param[out] language_object_name + /// If \b true is returned, the name of the artificial variable + /// for the language ("this" for C++, "self" for ObjC). + /// + /// @return + /// \b True if this symbol context represents a function that + /// is a method of a class, \b false otherwise. + //------------------------------------------------------------------ + bool + GetFunctionMethodInfo (lldb::LanguageType &language, + bool &is_instance_method, + ConstString &language_object_name); + + //------------------------------------------------------------------ + /// Find a name of the innermost function for the symbol context. + /// + /// For instance, if the symbol context contains an inlined block, + /// it will return the inlined function name. + /// + /// @param[in] prefer_mangled + /// if \btrue, then the mangled name will be returned if there + /// is one. Otherwise the unmangled name will be returned if it + /// is available. + /// + /// @return + /// The name of the function represented by this symbol context. + //------------------------------------------------------------------ + ConstString + GetFunctionName (Mangled::NamePreference preference = Mangled::ePreferDemangled) const; + + + //------------------------------------------------------------------ + /// Get the line entry that corresponds to the function. + /// + /// If the symbol context contains an inlined block, the line entry + /// for the start address of the inlined function will be returned, + /// otherwise the line entry for the start address of the function + /// will be returned. This can be used after doing a + /// Module::FindFunctions(...) or ModuleList::FindFunctions(...) + /// call in order to get the correct line table information for + /// the symbol context. + /// it will return the inlined function name. + /// + /// @param[in] prefer_mangled + /// if \btrue, then the mangled name will be returned if there + /// is one. Otherwise the unmangled name will be returned if it + /// is available. + /// + /// @return + /// The name of the function represented by this symbol context. + //------------------------------------------------------------------ + LineEntry + GetFunctionStartLineEntry () const; + + //------------------------------------------------------------------ + /// Find the block containing the inlined block that contains this block. + /// + /// For instance, if the symbol context contains an inlined block, + /// it will return the inlined function name. + /// + /// @param[in] curr_frame_pc + /// The address within the block of this object. + /// + /// @param[out] next_frame_sc + /// A new symbol context that does what the title says it does. + /// + /// @param[out] next_frame_addr + /// This is what you should report as the PC in \a next_frame_sc. + /// + /// @return + /// \b true if this SymbolContext specifies a block contained in an + /// inlined block. If this returns \b true, \a next_frame_sc and + /// \a next_frame_addr will be filled in correctly. + //------------------------------------------------------------------ + bool + GetParentOfInlinedScope (const Address &curr_frame_pc, + SymbolContext &next_frame_sc, + Address &inlined_frame_addr) const; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::TargetSP target_sp; ///< The Target for a given query + lldb::ModuleSP module_sp; ///< The Module for a given query + CompileUnit * comp_unit; ///< The CompileUnit for a given query + Function * function; ///< The Function for a given query + Block * block; ///< The Block for a given query + LineEntry line_entry; ///< The LineEntry for a given query + Symbol * symbol; ///< The Symbol for a given query +}; + + +class SymbolContextSpecifier +{ +public: + typedef enum SpecificationType + { + eNothingSpecified = 0, + eModuleSpecified = 1 << 0, + eFileSpecified = 1 << 1, + eLineStartSpecified = 1 << 2, + eLineEndSpecified = 1 << 3, + eFunctionSpecified = 1 << 4, + eClassOrNamespaceSpecified = 1 << 5, + eAddressRangeSpecified = 1 << 6 + } SpecificationType; + + // This one produces a specifier that matches everything... + SymbolContextSpecifier (const lldb::TargetSP& target_sp); + + ~SymbolContextSpecifier(); + + bool + AddSpecification (const char *spec_string, SpecificationType type); + + bool + AddLineSpecification (uint32_t line_no, SpecificationType type); + + void + Clear(); + + bool + SymbolContextMatches(SymbolContext &sc); + + bool + AddressMatches(lldb::addr_t addr); + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + +private: + lldb::TargetSP m_target_sp; + std::string m_module_spec; + lldb::ModuleSP m_module_sp; + std::unique_ptr m_file_spec_ap; + size_t m_start_line; + size_t m_end_line; + std::string m_function_spec; + std::string m_class_name; + std::unique_ptr m_address_range_ap; + uint32_t m_type; // Or'ed bits from SpecificationType + +}; + +//---------------------------------------------------------------------- +/// @class SymbolContextList SymbolContext.h "lldb/Symbol/SymbolContext.h" +/// @brief Defines a list of symbol context objects. +/// +/// This class provides a common structure that can be used to contain +/// the result of a query that can contain a multiple results. Examples +/// of such queries include: +/// @li Looking up a function by name. +/// @li Finding all addressses for a specified file and line number. +//---------------------------------------------------------------------- +class SymbolContextList +{ +public: + //------------------------------------------------------------------ + /// Default constructor. + /// + /// Initialize with an empty list. + //------------------------------------------------------------------ + SymbolContextList (); + + //------------------------------------------------------------------ + /// Destructor. + //------------------------------------------------------------------ + ~SymbolContextList (); + + //------------------------------------------------------------------ + /// Append a new symbol context to the list. + /// + /// @param[in] sc + /// A symbol context to append to the list. + //------------------------------------------------------------------ + void + Append (const SymbolContext& sc); + + void + Append (const SymbolContextList& sc_list); + + bool + AppendIfUnique (const SymbolContext& sc, + bool merge_symbol_into_function); + + bool + MergeSymbolContextIntoFunctionContext (const SymbolContext& symbol_sc, + uint32_t start_idx = 0, + uint32_t stop_idx = UINT32_MAX); + + uint32_t + AppendIfUnique (const SymbolContextList& sc_list, + bool merge_symbol_into_function); + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Clears the symbol context list. + //------------------------------------------------------------------ + void + Clear(); + + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of each symbol context in + /// the list to the supplied stream \a s. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump(Stream *s, Target *target) const; + + //------------------------------------------------------------------ + /// Get accessor for a symbol context at index \a idx. + /// + /// Dump a description of the contents of each symbol context in + /// the list to the supplied stream \a s. + /// + /// @param[in] idx + /// The zero based index into the symbol context list. + /// + /// @param[out] sc + /// A reference to the symbol context to fill in. + /// + /// @return + /// Returns \b true if \a idx was a valid index into this + /// symbol context list and \a sc was filled in, \b false + /// otherwise. + //------------------------------------------------------------------ + bool + GetContextAtIndex(size_t idx, SymbolContext& sc) const; + + //------------------------------------------------------------------ + /// Direct reference accessor for a symbol context at index \a idx. + /// + /// The index \a idx must be a valid index, no error checking will + /// be done to ensure that it is valid. + /// + /// @param[in] idx + /// The zero based index into the symbol context list. + /// + /// @return + /// A const reference to the symbol context to fill in. + //------------------------------------------------------------------ + SymbolContext& + operator [] (size_t idx) + { + return m_symbol_contexts[idx]; + } + + const SymbolContext& + operator [] (size_t idx) const + { + return m_symbol_contexts[idx]; + } + + //------------------------------------------------------------------ + /// Get accessor for the last symbol context in the list. + /// + /// @param[out] sc + /// A reference to the symbol context to fill in. + /// + /// @return + /// Returns \b true if \a sc was filled in, \b false if the + /// list is empty. + //------------------------------------------------------------------ + bool + GetLastContext(SymbolContext& sc) const; + + bool + RemoveContextAtIndex (size_t idx); + //------------------------------------------------------------------ + /// Get accessor for a symbol context list size. + /// + /// @return + /// Returns the number of symbol context objects in the list. + //------------------------------------------------------------------ + uint32_t + GetSize() const; + + uint32_t + NumLineEntriesWithLine (uint32_t line) const; + + void + GetDescription(Stream *s, + lldb::DescriptionLevel level, + Target *target) const; + +protected: + typedef std::vector collection; ///< The collection type for the list. + + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_symbol_contexts; ///< The list of symbol contexts. +}; + +bool operator== (const SymbolContext& lhs, const SymbolContext& rhs); +bool operator!= (const SymbolContext& lhs, const SymbolContext& rhs); + +bool operator== (const SymbolContextList& lhs, const SymbolContextList& rhs); +bool operator!= (const SymbolContextList& lhs, const SymbolContextList& rhs); + +} // namespace lldb_private + +#endif // liblldb_SymbolContext_h_ diff --git a/include/lldb/Symbol/SymbolContextScope.h b/include/lldb/Symbol/SymbolContextScope.h new file mode 100644 index 00000000000..693cc0131e2 --- /dev/null +++ b/include/lldb/Symbol/SymbolContextScope.h @@ -0,0 +1,137 @@ +//===-- SymbolContextScope.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolContextScope_h_ +#define liblldb_SymbolContextScope_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class SymbolContextScope SymbolContextScope.h "lldb/Symbol/SymbolContextScope.h" +/// @brief Inherit from this if your object is part of a symbol context +/// and can reconstruct its symbol context. +/// +/// Many objects that are part of a symbol context that have pointers +/// back to parent objects that own them. Any members of a symbol +/// context that, once they are built, will not go away, can inherit +/// from this pure virtual class and can then reconstruct their symbol +/// context without having to keep a complete SymbolContext object in +/// the object. +/// +/// Examples of these objects include: +/// @li Module +/// @li CompileUnit +/// @li Function +/// @li Block +/// @li Symbol +/// +/// Other objects can store a "SymbolContextScope *" using any pointers +/// to one of the above objects. This allows clients to hold onto a +/// pointer that uniquely will identify a symbol context. Those clients +/// can then always reconstruct the symbol context using the pointer, or +/// use it to uniquely identify a symbol context for an object. +/// +/// Example objects include that currently use "SymbolContextScope *" +/// objects include: +/// @li Variable objects that can reconstruct where they are scoped +/// by making sure the SymbolContextScope * comes from the scope +/// in which the variable was declared. If a variable is a global, +/// the appropriate CompileUnit * will be used when creating the +/// variable. A static function variables, can the Block scope +/// in which the variable is defined. Function arguments can use +/// the Function object as their scope. The SymbolFile parsers +/// will set these correctly as the variables are parsed. +/// @li Type objects that know exactly in which scope they +/// originated much like the variables above. +/// @li StackID objects that are able to know that if the CFA +/// (stack pointer at the beginning of a function) and the +/// start PC for the function/symbol and the SymbolContextScope +/// pointer (a unique pointer that identifies a symbol context +/// location) match within the same thread, that the stack +/// frame is the same as the previous stack frame. +/// +/// Objects that adhere to this protocol can reconstruct enough of a +/// symbol context to allow functions that take a symbol context to be +/// called. Lists can also be created using a SymbolContextScope* and +/// and object pairs that allow large collections of objects to be +/// passed around with minimal overhead. +//---------------------------------------------------------------------- +class SymbolContextScope +{ +public: + virtual + ~SymbolContextScope () {} + + //------------------------------------------------------------------ + /// Reconstruct the object's symbolc context into \a sc. + /// + /// The object should fill in as much of the SymbolContext as it + /// can so function calls that require a symbol context can be made + /// for the given object. + /// + /// @param[out] sc + /// A symbol context object pointer that gets filled in. + //------------------------------------------------------------------ + virtual void + CalculateSymbolContext (SymbolContext *sc) = 0; + + + virtual lldb::ModuleSP + CalculateSymbolContextModule () + { + return lldb::ModuleSP(); + } + + virtual CompileUnit * + CalculateSymbolContextCompileUnit () + { + return NULL; + } + + virtual Function * + CalculateSymbolContextFunction () + { + return NULL; + } + + virtual Block * + CalculateSymbolContextBlock () + { + return NULL; + } + + virtual Symbol * + CalculateSymbolContextSymbol () + { + return NULL; + } + + //------------------------------------------------------------------ + /// Dump the object's symbolc context to the stream \a s. + /// + /// The object should dump its symbol context to the stream \a s. + /// This function is widely used in the DumpDebug and verbose output + /// for lldb objets. + /// + /// @param[in] s + /// The stream to which to dump the object's symbol context. + //------------------------------------------------------------------ + virtual void + DumpSymbolContext (Stream *s) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_SymbolContextScope_h_ diff --git a/include/lldb/Symbol/SymbolFile.h b/include/lldb/Symbol/SymbolFile.h new file mode 100644 index 00000000000..5b774e3a7d1 --- /dev/null +++ b/include/lldb/Symbol/SymbolFile.h @@ -0,0 +1,167 @@ +//===-- SymbolFile.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolFile_h_ +#define liblldb_SymbolFile_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Type.h" + +namespace lldb_private { + +class SymbolFile : + public PluginInterface +{ +public: + //------------------------------------------------------------------ + // Symbol file ability bits. + // + // Each symbol file can claim to support one or more symbol file + // abilities. These get returned from SymbolFile::GetAbilities(). + // These help us to determine which plug-in will be best to load + // the debug information found in files. + //------------------------------------------------------------------ + enum Abilities + { + CompileUnits = (1u << 0), + LineTables = (1u << 1), + Functions = (1u << 2), + Blocks = (1u << 3), + GlobalVariables = (1u << 4), + LocalVariables = (1u << 5), + VariableTypes = (1u << 6), + kAllAbilities =((1u << 7) - 1u) + }; + + static SymbolFile * + FindPlugin (ObjectFile* obj_file); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolFile(ObjectFile* obj_file) : + m_obj_file(obj_file), + m_abilities(0), + m_calculated_abilities(false) + { + } + + virtual + ~SymbolFile() + { + } + + //------------------------------------------------------------------ + /// Get a mask of what this symbol file supports for the object file + /// that it was constructed with. + /// + /// Each symbol file gets to respond with a mask of abilities that + /// it supports for each object file. This happens when we are + /// trying to figure out which symbol file plug-in will get used + /// for a given object file. The plug-in that resoonds with the + /// best mix of "SymbolFile::Abilities" bits set, will get chosen to + /// be the symbol file parser. This allows each plug-in to check for + /// sections that contain data a symbol file plug-in would need. For + /// example the DWARF plug-in requires DWARF sections in a file that + /// contain debug information. If the DWARF plug-in doesn't find + /// these sections, it won't respond with many ability bits set, and + /// we will probably fall back to the symbol table SymbolFile plug-in + /// which uses any information in the symbol table. Also, plug-ins + /// might check for some specific symbols in a symbol table in the + /// case where the symbol table contains debug information (STABS + /// and COFF). Not a lot of work should happen in these functions + /// as the plug-in might not get selected due to another plug-in + /// having more abilities. Any initialization work should be saved + /// for "void SymbolFile::InitializeObject()" which will get called + /// on the SymbolFile object with the best set of abilities. + /// + /// @return + /// A uint32_t mask containing bits from the SymbolFile::Abilities + /// enumeration. Any bits that are set represent an ability that + /// this symbol plug-in can parse from the object file. + ///------------------------------------------------------------------ + uint32_t GetAbilities () + { + if (!m_calculated_abilities) + { + m_abilities = CalculateAbilities(); + m_calculated_abilities = true; + } + + return m_abilities; + } + + virtual uint32_t CalculateAbilities() = 0; + + //------------------------------------------------------------------ + /// Initialize the SymbolFile object. + /// + /// The SymbolFile object with the best set of abilities (detected + /// in "uint32_t SymbolFile::GetAbilities()) will have this function + /// called if it is chosen to parse an object file. More complete + /// initialization can happen in this function which will get called + /// prior to any other functions in the SymbolFile protocol. + //------------------------------------------------------------------ + virtual void InitializeObject() {} + + //------------------------------------------------------------------ + // Compile Unit function calls + //------------------------------------------------------------------ + // Approach 1 - iterator + virtual uint32_t GetNumCompileUnits() = 0; + virtual lldb::CompUnitSP ParseCompileUnitAtIndex(uint32_t index) = 0; + + virtual lldb::LanguageType ParseCompileUnitLanguage (const SymbolContext& sc) = 0; + virtual size_t ParseCompileUnitFunctions (const SymbolContext& sc) = 0; + virtual bool ParseCompileUnitLineTable (const SymbolContext& sc) = 0; + virtual bool ParseCompileUnitSupportFiles (const SymbolContext& sc, FileSpecList& support_files) = 0; + virtual size_t ParseFunctionBlocks (const SymbolContext& sc) = 0; + virtual size_t ParseTypes (const SymbolContext& sc) = 0; + virtual size_t ParseVariablesForContext (const SymbolContext& sc) = 0; + virtual Type* ResolveTypeUID (lldb::user_id_t type_uid) = 0; + virtual bool ResolveClangOpaqueTypeDefinition (ClangASTType &clang_type) = 0; + virtual clang::DeclContext* GetClangDeclContextForTypeUID (const lldb_private::SymbolContext &sc, lldb::user_id_t type_uid) { return NULL; } + virtual clang::DeclContext* GetClangDeclContextContainingTypeUID (lldb::user_id_t type_uid) { return NULL; } + virtual uint32_t ResolveSymbolContext (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) = 0; + virtual uint32_t ResolveSymbolContext (const FileSpec& file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList& sc_list) = 0; + virtual uint32_t FindGlobalVariables (const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, VariableList& variables) = 0; + virtual uint32_t FindGlobalVariables (const RegularExpression& regex, bool append, uint32_t max_matches, VariableList& variables) = 0; + virtual uint32_t FindFunctions (const ConstString &name, const ClangNamespaceDecl *namespace_decl, uint32_t name_type_mask, bool include_inlines, bool append, SymbolContextList& sc_list) = 0; + virtual uint32_t FindFunctions (const RegularExpression& regex, bool include_inlines, bool append, SymbolContextList& sc_list) = 0; + virtual uint32_t FindTypes (const SymbolContext& sc, const ConstString &name, const ClangNamespaceDecl *namespace_decl, bool append, uint32_t max_matches, TypeList& types) = 0; +// virtual uint32_t FindTypes (const SymbolContext& sc, const RegularExpression& regex, bool append, uint32_t max_matches, TypeList& types) = 0; + virtual TypeList * GetTypeList (); + virtual size_t GetTypes (lldb_private::SymbolContextScope *sc_scope, + uint32_t type_mask, + lldb_private::TypeList &type_list) = 0; + virtual ClangASTContext & + GetClangASTContext (); + virtual ClangNamespaceDecl + FindNamespace (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *parent_namespace_decl) = 0; + + ObjectFile* GetObjectFile() { return m_obj_file; } + const ObjectFile* GetObjectFile() const { return m_obj_file; } + +protected: + ObjectFile* m_obj_file; // The object file that symbols can be extracted from. + uint32_t m_abilities; + bool m_calculated_abilities; +private: + DISALLOW_COPY_AND_ASSIGN (SymbolFile); +}; + + +} // namespace lldb_private + +#endif // liblldb_SymbolFile_h_ diff --git a/include/lldb/Symbol/SymbolVendor.h b/include/lldb/Symbol/SymbolVendor.h new file mode 100644 index 00000000000..0eeea4eb466 --- /dev/null +++ b/include/lldb/Symbol/SymbolVendor.h @@ -0,0 +1,207 @@ +//===-- SymbolVendor.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SymbolVendor_h_ +#define liblldb_SymbolVendor_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/ModuleChild.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/TypeList.h" + + +namespace lldb_private { + +//---------------------------------------------------------------------- +// The symbol vendor class is designed to abstract the process of +// searching for debug information for a given module. Platforms can +// subclass this class and provide extra ways to find debug information. +// Examples would be a subclass that would allow for locating a stand +// alone debug file, parsing debug maps, or runtime data in the object +// files. A symbol vendor can use multiple sources (SymbolFile +// objects) to provide the information and only parse as deep as needed +// in order to provide the information that is requested. +//---------------------------------------------------------------------- +class SymbolVendor : + public ModuleChild, + public PluginInterface +{ +public: + static SymbolVendor* + FindPlugin (const lldb::ModuleSP &module_sp, + Stream *feedback_strm); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SymbolVendor(const lldb::ModuleSP &module_sp); + + virtual + ~SymbolVendor(); + + void + AddSymbolFileRepresentation(const lldb::ObjectFileSP &objfile_sp); + + virtual void + Dump(Stream *s); + + virtual lldb::LanguageType + ParseCompileUnitLanguage (const SymbolContext& sc); + + virtual size_t + ParseCompileUnitFunctions (const SymbolContext& sc); + + virtual bool + ParseCompileUnitLineTable (const SymbolContext& sc); + + virtual bool + ParseCompileUnitSupportFiles (const SymbolContext& sc, + FileSpecList& support_files); + + virtual size_t + ParseFunctionBlocks (const SymbolContext& sc); + + virtual size_t + ParseTypes (const SymbolContext& sc); + + virtual size_t + ParseVariablesForContext (const SymbolContext& sc); + + virtual Type* + ResolveTypeUID(lldb::user_id_t type_uid); + + virtual uint32_t + ResolveSymbolContext (const Address& so_addr, + uint32_t resolve_scope, + SymbolContext& sc); + + virtual uint32_t + ResolveSymbolContext (const FileSpec& file_spec, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list); + + virtual size_t + FindGlobalVariables (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variables); + + virtual size_t + FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variables); + + virtual size_t + FindFunctions (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + uint32_t name_type_mask, + bool include_inlines, + bool append, + SymbolContextList& sc_list); + + virtual size_t + FindFunctions (const RegularExpression& regex, + bool include_inlines, + bool append, + SymbolContextList& sc_list); + + virtual size_t + FindTypes (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types); + + virtual ClangNamespaceDecl + FindNamespace (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *parent_namespace_decl); + + virtual size_t + GetNumCompileUnits(); + + virtual bool + SetCompileUnitAtIndex (size_t cu_idx, + const lldb::CompUnitSP &cu_sp); + + virtual lldb::CompUnitSP + GetCompileUnitAtIndex(size_t idx); + + TypeList& + GetTypeList() + { + return m_type_list; + } + + const TypeList& + GetTypeList() const + { + return m_type_list; + } + + virtual size_t + GetTypes (SymbolContextScope *sc_scope, + uint32_t type_mask, + TypeList &type_list); + + SymbolFile * + GetSymbolFile() + { + return m_sym_file_ap.get(); + } + + // Get module unified section list symbol table. + virtual Symtab * + GetSymtab (); + + // Clear module unified section list symbol table. + virtual void + ClearSymtab (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from SymbolVendor can see and modify these + //------------------------------------------------------------------ + typedef std::vector CompileUnits; + typedef CompileUnits::iterator CompileUnitIter; + typedef CompileUnits::const_iterator CompileUnitConstIter; + + TypeList m_type_list; // Uniqued types for all parsers owned by this module + CompileUnits m_compile_units; // The current compile units + lldb::ObjectFileSP m_objfile_sp; // Keep a reference to the object file in case it isn't the same as the module object file (debug symbols in a separate file) + std::unique_ptr m_sym_file_ap; // A single symbol file. Subclasses can add more of these if needed. + +private: + //------------------------------------------------------------------ + // For SymbolVendor only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (SymbolVendor); +}; + + +} // namespace lldb_private + +#endif // liblldb_SymbolVendor_h_ diff --git a/include/lldb/Symbol/Symtab.h b/include/lldb/Symbol/Symtab.h new file mode 100644 index 00000000000..666c3b5686b --- /dev/null +++ b/include/lldb/Symbol/Symtab.h @@ -0,0 +1,160 @@ +//===-- Symtab.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_Symtab_h_ +#define liblldb_Symtab_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/Symbol.h" + +namespace lldb_private { + +class Symtab +{ +public: + typedef std::vector IndexCollection; + typedef UniqueCStringMap NameToIndexMap; + + typedef enum Debug { + eDebugNo, // Not a debug symbol + eDebugYes, // A debug symbol + eDebugAny + } Debug; + + typedef enum Visibility { + eVisibilityAny, + eVisibilityExtern, + eVisibilityPrivate + } Visibility; + + Symtab(ObjectFile *objfile); + ~Symtab(); + + void Reserve (size_t count); + Symbol * Resize (size_t count); + uint32_t AddSymbol(const Symbol& symbol); + size_t GetNumSymbols() const; + void Dump(Stream *s, Target *target, SortOrder sort_type); + void Dump(Stream *s, Target *target, std::vector& indexes) const; + uint32_t GetIndexForSymbol (const Symbol *symbol) const; + Mutex & GetMutex () + { + return m_mutex; + } + Symbol * FindSymbolByID (lldb::user_id_t uid) const; + Symbol * SymbolAtIndex (size_t idx); + const Symbol * SymbolAtIndex (size_t idx) const; + Symbol * FindSymbolWithType (lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, uint32_t &start_idx); + uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, std::vector& indexes, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; + uint32_t AppendSymbolIndexesWithTypeAndFlagsValue (lldb::SymbolType symbol_type, uint32_t flags_value, std::vector& indexes, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; + uint32_t AppendSymbolIndexesWithType (lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches, uint32_t start_idx = 0, uint32_t end_index = UINT32_MAX) const; + uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, std::vector& matches); + uint32_t AppendSymbolIndexesWithName (const ConstString& symbol_name, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches); + uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, std::vector& matches); + uint32_t AppendSymbolIndexesWithNameAndType (const ConstString& symbol_name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& matches); + uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, std::vector& indexes); + uint32_t AppendSymbolIndexesMatchingRegExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& indexes); + size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, std::vector& symbol_indexes); + size_t FindAllSymbolsWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes); + size_t FindAllSymbolsMatchingRexExAndType (const RegularExpression ®ex, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility, std::vector& symbol_indexes); + Symbol * FindFirstSymbolWithNameAndType (const ConstString &name, lldb::SymbolType symbol_type, Debug symbol_debug_type, Visibility symbol_visibility); + Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr, const uint32_t* indexes, uint32_t num_indexes); + Symbol * FindSymbolContainingFileAddress (lldb::addr_t file_addr); + size_t FindFunctionSymbols (const ConstString &name, uint32_t name_type_mask, SymbolContextList& sc_list); + void CalculateSymbolSizes (); + + void SortSymbolIndexesByValue (std::vector& indexes, bool remove_duplicates) const; + + static void DumpSymbolHeader (Stream *s); + + + void Finalize () + { + // Shrink to fit the symbols so we don't waste memory + if (m_symbols.capacity() > m_symbols.size()) + { + collection new_symbols (m_symbols.begin(), m_symbols.end()); + m_symbols.swap (new_symbols); + } + } + + void AppendSymbolNamesToMap (const IndexCollection &indexes, + bool add_demangled, + bool add_mangled, + NameToIndexMap &name_to_index_map) const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + typedef RangeDataVector FileRangeToIndexMap; + void InitNameIndexes (); + void InitAddressIndexes (); + + ObjectFile * m_objfile; + collection m_symbols; + FileRangeToIndexMap m_file_addr_to_index; + UniqueCStringMap m_name_to_index; + UniqueCStringMap m_basename_to_index; + UniqueCStringMap m_method_to_index; + UniqueCStringMap m_selector_to_index; + mutable Mutex m_mutex; // Provide thread safety for this symbol table + bool m_file_addr_to_index_computed:1, + m_name_indexes_computed:1; +private: + + bool + CheckSymbolAtIndex (size_t idx, Debug symbol_debug_type, Visibility symbol_visibility) const + { + switch (symbol_debug_type) + { + case eDebugNo: + if (m_symbols[idx].IsDebug() == true) + return false; + break; + + case eDebugYes: + if (m_symbols[idx].IsDebug() == false) + return false; + break; + + case eDebugAny: + break; + } + + switch (symbol_visibility) + { + case eVisibilityAny: + return true; + + case eVisibilityExtern: + return m_symbols[idx].IsExternal(); + + case eVisibilityPrivate: + return !m_symbols[idx].IsExternal(); + } + return false; + } + + void + SymbolIndicesToSymbolContextList (std::vector &symbol_indexes, + SymbolContextList &sc_list); + + DISALLOW_COPY_AND_ASSIGN (Symtab); +}; + +} // namespace lldb_private + +#endif // liblldb_Symtab_h_ diff --git a/include/lldb/Symbol/TaggedASTType.h b/include/lldb/Symbol/TaggedASTType.h new file mode 100644 index 00000000000..c44a5356f86 --- /dev/null +++ b/include/lldb/Symbol/TaggedASTType.h @@ -0,0 +1,61 @@ +//===-- TaggedASTType.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TaggedASTType_h_ +#define liblldb_TaggedASTType_h_ + +#include "lldb/Symbol/ClangASTType.h" + +namespace lldb_private +{ + +// For cases in which there are multiple classes of types that are not +// interchangeable, to allow static type checking. +template class TaggedASTType : public ClangASTType +{ +public: + TaggedASTType (const ClangASTType &clang_type) : + ClangASTType(clang_type) + { + } + + TaggedASTType (lldb::clang_type_t type, clang::ASTContext *ast_context) : + ClangASTType(ast_context, type) + { + } + + TaggedASTType (const TaggedASTType &tw) : + ClangASTType(tw) + { + } + + TaggedASTType () : + ClangASTType() + { + } + + virtual + ~TaggedASTType() + { + } + + TaggedASTType &operator= (const TaggedASTType &tw) + { + ClangASTType::operator= (tw); + return *this; + } +}; + +// Commonly-used tagged types, so code using them is interoperable +typedef TaggedASTType<0> TypeFromParser; +typedef TaggedASTType<1> TypeFromUser; + +} + +#endif diff --git a/include/lldb/Symbol/Type.h b/include/lldb/Symbol/Type.h new file mode 100644 index 00000000000..50b22fe96b9 --- /dev/null +++ b/include/lldb/Symbol/Type.h @@ -0,0 +1,596 @@ +//===-- Type.h --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Type_h_ +#define liblldb_Type_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ClangForward.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/UserID.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/Declaration.h" + +#include + +namespace lldb_private { + +class SymbolFileType : + public std::enable_shared_from_this, + public UserID + { + public: + SymbolFileType (SymbolFile &symbol_file, lldb::user_id_t uid) : + UserID (uid), + m_symbol_file (symbol_file) + { + } + + ~SymbolFileType () + { + } + + Type * + operator->() + { + return GetType (); + } + + Type * + GetType (); + + protected: + SymbolFile &m_symbol_file; + lldb::TypeSP m_type_sp; + }; + +class Type : + public std::enable_shared_from_this, + public UserID +{ +public: + typedef enum EncodingDataTypeTag + { + eEncodingInvalid, + eEncodingIsUID, ///< This type is the type whose UID is m_encoding_uid + eEncodingIsConstUID, ///< This type is the type whose UID is m_encoding_uid with the const qualifier added + eEncodingIsRestrictUID, ///< This type is the type whose UID is m_encoding_uid with the restrict qualifier added + eEncodingIsVolatileUID, ///< This type is the type whose UID is m_encoding_uid with the volatile qualifier added + eEncodingIsTypedefUID, ///< This type is pointer to a type whose UID is m_encoding_uid + eEncodingIsPointerUID, ///< This type is pointer to a type whose UID is m_encoding_uid + eEncodingIsLValueReferenceUID, ///< This type is L value reference to a type whose UID is m_encoding_uid + eEncodingIsRValueReferenceUID, ///< This type is R value reference to a type whose UID is m_encoding_uid + eEncodingIsSyntheticUID + } EncodingDataType; + + typedef enum ResolveStateTag + { + eResolveStateUnresolved = 0, + eResolveStateForward = 1, + eResolveStateLayout = 2, + eResolveStateFull = 3 + } ResolveState; + + Type (lldb::user_id_t uid, + SymbolFile* symbol_file, + const ConstString &name, + uint64_t byte_size, + SymbolContextScope *context, + lldb::user_id_t encoding_uid, + EncodingDataType encoding_uid_type, + const Declaration& decl, + const ClangASTType &clang_qual_type, + ResolveState clang_type_resolve_state); + + // This makes an invalid type. Used for functions that return a Type when they + // get an error. + Type(); + + Type (const Type &rhs); + + const Type& + operator= (const Type& rhs); + + void + Dump(Stream *s, bool show_context); + + void + DumpTypeName(Stream *s); + + + void + GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_name); + + SymbolFile * + GetSymbolFile() + { + return m_symbol_file; + } + const SymbolFile * + GetSymbolFile() const + { + return m_symbol_file; + } + + TypeList* + GetTypeList(); + + const ConstString& + GetName(); + + uint64_t + GetByteSize(); + + uint32_t + GetNumChildren (bool omit_empty_base_classes); + + bool + IsAggregateType (); + + bool + IsValidType () + { + return m_encoding_uid_type != eEncodingInvalid; + } + + bool + IsTypedef () + { + return m_encoding_uid_type == eEncodingIsTypedefUID; + } + + lldb::TypeSP + GetTypedefType(); + + const ConstString & + GetName () const + { + return m_name; + } + + ConstString + GetQualifiedName (); + + void + DumpValue(ExecutionContext *exe_ctx, + Stream *s, + const DataExtractor &data, + uint32_t data_offset, + bool show_type, + bool show_summary, + bool verbose, + lldb::Format format = lldb::eFormatDefault); + + bool + DumpValueInMemory(ExecutionContext *exe_ctx, + Stream *s, + lldb::addr_t address, + AddressType address_type, + bool show_types, + bool show_summary, + bool verbose); + + bool + ReadFromMemory (ExecutionContext *exe_ctx, + lldb::addr_t address, + AddressType address_type, + DataExtractor &data); + + bool + WriteToMemory (ExecutionContext *exe_ctx, + lldb::addr_t address, + AddressType address_type, + DataExtractor &data); + + bool + GetIsDeclaration() const; + + void + SetIsDeclaration(bool b); + + bool + GetIsExternal() const; + + void + SetIsExternal(bool b); + + lldb::Format + GetFormat (); + + lldb::Encoding + GetEncoding (uint64_t &count); + + SymbolContextScope * + GetSymbolContextScope() + { + return m_context; + } + const SymbolContextScope * + GetSymbolContextScope() const + { + return m_context; + } + void + SetSymbolContextScope(SymbolContextScope *context) + { + m_context = context; + } + + const lldb_private::Declaration & + GetDeclaration () const; + + // Get the clang type, and resolve definitions for any + // class/struct/union/enum types completely. + ClangASTType + GetClangFullType (); + + // Get the clang type, and resolve definitions enough so that the type could + // have layout performed. This allows ptrs and refs to class/struct/union/enum + // types remain forward declarations. + ClangASTType + GetClangLayoutType (); + + // Get the clang type and leave class/struct/union/enum types as forward + // declarations if they haven't already been fully defined. + ClangASTType + GetClangForwardType (); + + ClangASTContext & + GetClangASTContext (); + + static int + Compare(const Type &a, const Type &b); + + // From a fully qualified typename, split the type into the type basename + // and the remaining type scope (namespaces/classes). + static bool + GetTypeScopeAndBasename (const char* &name_cstr, + std::string &scope, + std::string &basename, + lldb::TypeClass &type_class); + void + SetEncodingType (Type *encoding_type) + { + m_encoding_type = encoding_type; + } + + uint32_t + GetEncodingMask (); + + ClangASTType + CreateClangTypedefType (Type *typedef_type, Type *base_type); + + bool + IsRealObjCClass(); + + bool + IsCompleteObjCClass() + { + return m_flags.is_complete_objc_class; + } + + void + SetIsCompleteObjCClass(bool is_complete_objc_class) + { + m_flags.is_complete_objc_class = is_complete_objc_class; + } + +protected: + ConstString m_name; + SymbolFile *m_symbol_file; + SymbolContextScope *m_context; // The symbol context in which this type is defined + Type *m_encoding_type; + lldb::user_id_t m_encoding_uid; + EncodingDataType m_encoding_uid_type; + uint64_t m_byte_size; + Declaration m_decl; + ClangASTType m_clang_type; + + struct Flags { + ResolveState clang_type_resolve_state : 2; + bool is_complete_objc_class : 1; + } m_flags; + + Type * + GetEncodingType (); + + bool + ResolveClangType (ResolveState clang_type_resolve_state); +}; + + +/// +/// Sometimes you can find the name of the type corresponding to an object, but we don't have debug +/// information for it. If that is the case, you can return one of these objects, and then if it +/// has a full type, you can use that, but if not at least you can print the name for informational +/// purposes. +/// + +class TypeAndOrName +{ +public: + TypeAndOrName (); + TypeAndOrName (lldb::TypeSP &type_sp); + TypeAndOrName (const char *type_str); + TypeAndOrName (const TypeAndOrName &rhs); + TypeAndOrName (ConstString &type_const_string); + + TypeAndOrName & + operator= (const TypeAndOrName &rhs); + + bool + operator==(const TypeAndOrName &other) const; + + bool + operator!=(const TypeAndOrName &other) const; + + ConstString GetName () const; + + lldb::TypeSP + GetTypeSP () const + { + return m_type_sp; + } + + void + SetName (const ConstString &type_name); + + void + SetName (const char *type_name_cstr); + + void + SetTypeSP (lldb::TypeSP type_sp); + + bool + IsEmpty (); + + bool + HasName (); + + bool + HasTypeSP (); + + void + Clear (); + + operator + bool () + { + return !IsEmpty(); + } + +private: + lldb::TypeSP m_type_sp; + ConstString m_type_name; +}; + +// the two classes here are used by the public API as a backend to +// the SBType and SBTypeList classes + +class TypeImpl +{ +public: + + TypeImpl() : + m_clang_ast_type(), + m_type_sp() + { + } + + TypeImpl(const TypeImpl& rhs) : + m_clang_ast_type(rhs.m_clang_ast_type), + m_type_sp(rhs.m_type_sp) + { + } + + TypeImpl(const lldb_private::ClangASTType& type); + + TypeImpl(const lldb::TypeSP& type); + + TypeImpl& + operator = (const TypeImpl& rhs); + + bool + operator == (const TypeImpl& rhs) + { + return m_clang_ast_type == rhs.m_clang_ast_type && m_type_sp.get() == rhs.m_type_sp.get(); + } + + bool + operator != (const TypeImpl& rhs) + { + return m_clang_ast_type != rhs.m_clang_ast_type || m_type_sp.get() != rhs.m_type_sp.get(); + } + + bool + IsValid() + { + return m_type_sp.get() != NULL || m_clang_ast_type.IsValid(); + } + + const lldb_private::ClangASTType & + GetClangASTType() const + { + return m_clang_ast_type; + } + + clang::ASTContext* + GetASTContext(); + + lldb::clang_type_t + GetOpaqueQualType(); + + lldb::TypeSP + GetTypeSP () + { + return m_type_sp; + } + + ConstString + GetName (); + + bool + GetDescription (lldb_private::Stream &strm, + lldb::DescriptionLevel description_level); + + void + SetType (const lldb::TypeSP &type_sp); + +private: + ClangASTType m_clang_ast_type; + lldb::TypeSP m_type_sp; +}; + +class TypeListImpl +{ +public: + TypeListImpl() : + m_content() + { + } + + void + Append (const lldb::TypeImplSP& type) + { + m_content.push_back(type); + } + + class AppendVisitor + { + public: + AppendVisitor(TypeListImpl &type_list) : + m_type_list(type_list) + { + } + + void + operator() (const lldb::TypeImplSP& type) + { + m_type_list.Append(type); + } + + private: + TypeListImpl &m_type_list; + }; + + void + Append (const lldb_private::TypeList &type_list); + + lldb::TypeImplSP + GetTypeAtIndex(size_t idx) + { + lldb::TypeImplSP type_sp; + if (idx < GetSize()) + type_sp = m_content[idx]; + return type_sp; + } + + size_t + GetSize() + { + return m_content.size(); + } + +private: + std::vector m_content; +}; + +class TypeMemberImpl +{ +public: + TypeMemberImpl () : + m_type_impl_sp (), + m_bit_offset (0), + m_name (), + m_bitfield_bit_size (0), + m_is_bitfield (false) + + { + } + + TypeMemberImpl (const lldb::TypeImplSP &type_impl_sp, + uint64_t bit_offset, + const ConstString &name, + uint32_t bitfield_bit_size = 0, + bool is_bitfield = false) : + m_type_impl_sp (type_impl_sp), + m_bit_offset (bit_offset), + m_name (name), + m_bitfield_bit_size (bitfield_bit_size), + m_is_bitfield (is_bitfield) + { + } + + TypeMemberImpl (const lldb::TypeImplSP &type_impl_sp, + uint64_t bit_offset): + m_type_impl_sp (type_impl_sp), + m_bit_offset (bit_offset), + m_name (), + m_bitfield_bit_size (0), + m_is_bitfield (false) + { + if (m_type_impl_sp) + m_name = m_type_impl_sp->GetName(); + } + + const lldb::TypeImplSP & + GetTypeImpl () + { + return m_type_impl_sp; + } + + const ConstString & + GetName () const + { + return m_name; + } + + uint64_t + GetBitOffset () const + { + return m_bit_offset; + } + + uint32_t + GetBitfieldBitSize () const + { + return m_bitfield_bit_size; + } + + void + SetBitfieldBitSize (uint32_t bitfield_bit_size) + { + m_bitfield_bit_size = bitfield_bit_size; + } + + bool + GetIsBitfield () const + { + return m_is_bitfield; + } + + void + SetIsBitfield (bool is_bitfield) + { + m_is_bitfield = is_bitfield; + } + +protected: + lldb::TypeImplSP m_type_impl_sp; + uint64_t m_bit_offset; + ConstString m_name; + uint32_t m_bitfield_bit_size; // Bit size for bitfield members only + bool m_is_bitfield; +}; + + +} // namespace lldb_private + +#endif // liblldb_Type_h_ + diff --git a/include/lldb/Symbol/TypeList.h b/include/lldb/Symbol/TypeList.h new file mode 100644 index 00000000000..9c74db6bf1f --- /dev/null +++ b/include/lldb/Symbol/TypeList.h @@ -0,0 +1,88 @@ +//===-- TypeList.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TypeList_h_ +#define liblldb_TypeList_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/Type.h" +#include + +namespace lldb_private { + +class TypeList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TypeList(); + + virtual + ~TypeList(); + + void + Clear(); + + void + Dump(Stream *s, bool show_context); + +// lldb::TypeSP +// FindType(lldb::user_id_t uid); + + TypeList + FindTypes(const ConstString &name); + + void + Insert (const lldb::TypeSP& type); + + bool + InsertUnique (const lldb::TypeSP& type); + + uint32_t + GetSize() const; + + lldb::TypeSP + GetTypeAtIndex(uint32_t idx); + + void + ForEach (std::function const &callback) const; + + void + ForEach (std::function const &callback); + + bool + RemoveTypeWithUID (lldb::user_id_t uid); + + void + RemoveMismatchedTypes (const char *qualified_typename, + bool exact_match); + + void + RemoveMismatchedTypes (const std::string &type_scope, + const std::string &type_basename, + lldb::TypeClass type_class, + bool exact_match); + + void + RemoveMismatchedTypes (lldb::TypeClass type_class); + +private: + typedef std::multimap collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_types; + + DISALLOW_COPY_AND_ASSIGN (TypeList); +}; + +} // namespace lldb_private + +#endif // liblldb_TypeList_h_ diff --git a/include/lldb/Symbol/TypeVendor.h b/include/lldb/Symbol/TypeVendor.h new file mode 100644 index 00000000000..559b21eeb95 --- /dev/null +++ b/include/lldb/Symbol/TypeVendor.h @@ -0,0 +1,61 @@ +//===-- TypeVendor.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TypeVendor_h_ +#define liblldb_TypeVendor_h_ + +#include "lldb/Core/ClangForward.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// The type vendor class is intended as a generic interface to search +// for Clang types that are not necessarily backed by a specific symbol +// file. +//---------------------------------------------------------------------- +class TypeVendor +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + TypeVendor() + { + } + + virtual + ~TypeVendor() + { + } + + virtual uint32_t + FindTypes (const ConstString &name, + bool append, + uint32_t max_matches, + std::vector &types) = 0; + + virtual clang::ASTContext * + GetClangASTContext () = 0; + +protected: + //------------------------------------------------------------------ + // Classes that inherit from TypeVendor can see and modify these + //------------------------------------------------------------------ + +private: + //------------------------------------------------------------------ + // For TypeVendor only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (TypeVendor); +}; + + +} // namespace lldb_private + +#endif diff --git a/include/lldb/Symbol/UnwindPlan.h b/include/lldb/Symbol/UnwindPlan.h new file mode 100644 index 00000000000..6fc5ce04235 --- /dev/null +++ b/include/lldb/Symbol/UnwindPlan.h @@ -0,0 +1,499 @@ +#ifndef liblldb_UnwindPlan_h +#define liblldb_UnwindPlan_h + +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ConstString.h" + +#include +#include + +namespace lldb_private { + +// The UnwindPlan object specifies how to unwind out of a function - where +// this function saves the caller's register values before modifying them +// (for non-volatile aka saved registers) and how to find this frame's +// Canonical Frame Address (CFA). + +// Most commonly, registers are saved on the stack, offset some bytes from +// the Canonical Frame Address, or CFA, which is the starting address of +// this function's stack frame (the CFA is same as the eh_frame's CFA, +// whatever that may be on a given architecture). +// The CFA address for the stack frame does not change during +// the lifetime of the function. + +// Internally, the UnwindPlan is structured as a vector of register locations +// organized by code address in the function, showing which registers have been +// saved at that point and where they are saved. +// It can be thought of as the expanded table form of the DWARF CFI +// encoded information. + +// Other unwind information sources will be converted into UnwindPlans before +// being added to a FuncUnwinders object. The unwind source may be +// an eh_frame FDE, a DWARF debug_frame FDE, or assembly language based +// prologue analysis. +// The UnwindPlan is the canonical form of this information that the unwinder +// code will use when walking the stack. + +class UnwindPlan { +public: + + class Row { + public: + class RegisterLocation + { + public: + + enum RestoreType + { + unspecified, // not specified, we may be able to assume this + // is the same register. gcc doesn't specify all + // initial values so we really don't know... + undefined, // reg is not available, e.g. volatile reg + same, // reg is unchanged + atCFAPlusOffset, // reg = deref(CFA + offset) + isCFAPlusOffset, // reg = CFA + offset + inOtherRegister, // reg = other reg + atDWARFExpression, // reg = deref(eval(dwarf_expr)) + isDWARFExpression // reg = eval(dwarf_expr) + }; + + RegisterLocation() : + m_type(unspecified), + m_location() + { + } + + bool + operator == (const RegisterLocation& rhs) const; + + bool + operator != (const RegisterLocation &rhs) const + { + return !(*this == rhs); + } + + void + SetUnspecified() + { + m_type = unspecified; + } + + void + SetUndefined() + { + m_type = undefined; + } + + void + SetSame() + { + m_type = same; + } + + bool + IsSame () const + { + return m_type == same; + } + + bool + IsUnspecified () const + { + return m_type == unspecified; + } + + bool + IsCFAPlusOffset () const + { + return m_type == isCFAPlusOffset; + } + + bool + IsAtCFAPlusOffset () const + { + return m_type == atCFAPlusOffset; + } + + bool + IsInOtherRegister () const + { + return m_type == inOtherRegister; + } + + bool + IsAtDWARFExpression () const + { + return m_type == atDWARFExpression; + } + + bool + IsDWARFExpression () const + { + return m_type == isDWARFExpression; + } + + void + SetAtCFAPlusOffset (int32_t offset) + { + m_type = atCFAPlusOffset; + m_location.offset = offset; + } + + void + SetIsCFAPlusOffset (int32_t offset) + { + m_type = isCFAPlusOffset; + m_location.offset = offset; + } + + void + SetInRegister (uint32_t reg_num) + { + m_type = inOtherRegister; + m_location.reg_num = reg_num; + } + + uint32_t + GetRegisterNumber () const + { + if (m_type == inOtherRegister) + return m_location.reg_num; + return LLDB_INVALID_REGNUM; + } + + RestoreType + GetLocationType () const + { + return m_type; + } + + int32_t + GetOffset () const + { + if (m_type == atCFAPlusOffset || m_type == isCFAPlusOffset) + return m_location.offset; + return 0; + } + + void + GetDWARFExpr (const uint8_t **opcodes, uint16_t& len) const + { + if (m_type == atDWARFExpression || m_type == isDWARFExpression) + { + *opcodes = m_location.expr.opcodes; + len = m_location.expr.length; + } + else + { + *opcodes = NULL; + len = 0; + } + } + + void + SetAtDWARFExpression (const uint8_t *opcodes, uint32_t len); + + void + SetIsDWARFExpression (const uint8_t *opcodes, uint32_t len); + + const uint8_t * + GetDWARFExpressionBytes () + { + if (m_type == atDWARFExpression || m_type == isDWARFExpression) + return m_location.expr.opcodes; + return NULL; + } + + int + GetDWARFExpressionLength () + { + if (m_type == atDWARFExpression || m_type == isDWARFExpression) + return m_location.expr.length; + return 0; + } + + void + Dump (Stream &s, + const UnwindPlan* unwind_plan, + const UnwindPlan::Row* row, + Thread* thread, + bool verbose) const; + + private: + RestoreType m_type; // How do we locate this register? + union + { + // For m_type == atCFAPlusOffset or m_type == isCFAPlusOffset + int32_t offset; + // For m_type == inOtherRegister + uint32_t reg_num; // The register number + // For m_type == atDWARFExpression or m_type == isDWARFExpression + struct { + const uint8_t *opcodes; + uint16_t length; + } expr; + } m_location; + }; + + public: + Row (); + + Row (const UnwindPlan::Row& rhs) : + m_offset (rhs.m_offset), + m_cfa_reg_num (rhs.m_cfa_reg_num), + m_cfa_offset (rhs.m_cfa_offset), + m_register_locations (rhs.m_register_locations) + { + } + + bool + operator == (const Row &rhs) const; + + bool + GetRegisterInfo (uint32_t reg_num, RegisterLocation& register_location) const; + + void + SetRegisterInfo (uint32_t reg_num, const RegisterLocation register_location); + + lldb::addr_t + GetOffset() const + { + return m_offset; + } + + void + SetOffset(lldb::addr_t offset) + { + m_offset = offset; + } + + void + SlideOffset(lldb::addr_t offset) + { + m_offset += offset; + } + + uint32_t + GetCFARegister () const + { + return m_cfa_reg_num; + } + + bool + SetRegisterLocationToAtCFAPlusOffset (uint32_t reg_num, + int32_t offset, + bool can_replace); + + bool + SetRegisterLocationToIsCFAPlusOffset (uint32_t reg_num, + int32_t offset, + bool can_replace); + + bool + SetRegisterLocationToUndefined (uint32_t reg_num, + bool can_replace, + bool can_replace_only_if_unspecified); + + bool + SetRegisterLocationToUnspecified (uint32_t reg_num, + bool can_replace); + + bool + SetRegisterLocationToRegister (uint32_t reg_num, + uint32_t other_reg_num, + bool can_replace); + + bool + SetRegisterLocationToSame (uint32_t reg_num, + bool must_replace); + + + + void + SetCFARegister (uint32_t reg_num); + + int32_t + GetCFAOffset () const + { + return m_cfa_offset; + } + + void + SetCFAOffset (int32_t offset) + { + m_cfa_offset = offset; + } + + void + Clear (); + + void + Dump (Stream& s, const UnwindPlan* unwind_plan, Thread* thread, lldb::addr_t base_addr) const; + + protected: + typedef std::map collection; + lldb::addr_t m_offset; // Offset into the function for this row + uint32_t m_cfa_reg_num; // The Call Frame Address register number + int32_t m_cfa_offset; // The offset from the CFA for this row + collection m_register_locations; + }; // class Row + +public: + + typedef std::shared_ptr RowSP; + + UnwindPlan (lldb::RegisterKind reg_kind) : + m_row_list (), + m_plan_valid_address_range (), + m_register_kind (reg_kind), + m_return_addr_register (LLDB_INVALID_REGNUM), + m_source_name (), + m_plan_is_sourced_from_compiler (eLazyBoolCalculate), + m_plan_is_valid_at_all_instruction_locations (eLazyBoolCalculate) + { + } + + ~UnwindPlan () + { + } + + void + Dump (Stream& s, Thread* thread, lldb::addr_t base_addr) const; + + void + AppendRow (const RowSP& row_sp); + + // Returns a pointer to the best row for the given offset into the function's instructions. + // If offset is -1 it indicates that the function start is unknown - the final row in the UnwindPlan is returned. + // In practice, the UnwindPlan for a function with no known start address will be the architectural default + // UnwindPlan which will only have one row. + UnwindPlan::RowSP + GetRowForFunctionOffset (int offset) const; + + lldb::RegisterKind + GetRegisterKind () const + { + return m_register_kind; + } + + void + SetRegisterKind (lldb::RegisterKind kind) + { + m_register_kind = kind; + } + + void + SetReturnAddressRegister (uint32_t regnum) + { + m_return_addr_register = regnum; + } + + uint32_t + GetReturnAddressRegister (void) + { + return m_return_addr_register; + } + + uint32_t + GetInitialCFARegister () const + { + if (m_row_list.empty()) + return LLDB_INVALID_REGNUM; + return m_row_list.front()->GetCFARegister(); + } + + // This UnwindPlan may not be valid at every address of the function span. + // For instance, a FastUnwindPlan will not be valid at the prologue setup + // instructions - only in the body of the function. + void + SetPlanValidAddressRange (const AddressRange& range); + + const AddressRange & + GetAddressRange () const + { + return m_plan_valid_address_range; + } + + bool + PlanValidAtAddress (Address addr); + + bool + IsValidRowIndex (uint32_t idx) const; + + const UnwindPlan::RowSP + GetRowAtIndex (uint32_t idx) const; + + const UnwindPlan::RowSP + GetLastRow () const; + + lldb_private::ConstString + GetSourceName () const; + + void + SetSourceName (const char *); + + // Was this UnwindPlan emitted by a compiler? + lldb_private::LazyBool + GetSourcedFromCompiler () const + { + return m_plan_is_sourced_from_compiler; + } + + // Was this UnwindPlan emitted by a compiler? + void + SetSourcedFromCompiler (lldb_private::LazyBool from_compiler) + { + m_plan_is_sourced_from_compiler = from_compiler; + } + + // Is this UnwindPlan valid at all instructions? If not, then it is assumed valid at call sites, + // e.g. for exception handling. + lldb_private::LazyBool + GetUnwindPlanValidAtAllInstructions () const + { + return m_plan_is_valid_at_all_instruction_locations; + } + + // Is this UnwindPlan valid at all instructions? If not, then it is assumed valid at call sites, + // e.g. for exception handling. + void + SetUnwindPlanValidAtAllInstructions (lldb_private::LazyBool valid_at_all_insn) + { + m_plan_is_valid_at_all_instruction_locations = valid_at_all_insn; + } + + int + GetRowCount () const; + + void + Clear() + { + m_row_list.clear(); + m_plan_valid_address_range.Clear(); + m_register_kind = lldb::eRegisterKindDWARF; + m_source_name.Clear(); + } + + const RegisterInfo * + GetRegisterInfo (Thread* thread, uint32_t reg_num) const; + +private: + + + typedef std::vector collection; + collection m_row_list; + AddressRange m_plan_valid_address_range; + lldb::RegisterKind m_register_kind; // The RegisterKind these register numbers are in terms of - will need to be + // translated to lldb native reg nums at unwind time + uint32_t m_return_addr_register; // The register that has the return address for the caller frame + // e.g. the lr on arm + lldb_private::ConstString m_source_name; // for logging, where this UnwindPlan originated from + lldb_private::LazyBool m_plan_is_sourced_from_compiler; + lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations; +}; // class UnwindPlan + +} // namespace lldb_private + +#endif //liblldb_UnwindPlan_h diff --git a/include/lldb/Symbol/UnwindTable.h b/include/lldb/Symbol/UnwindTable.h new file mode 100644 index 00000000000..cefb91eb371 --- /dev/null +++ b/include/lldb/Symbol/UnwindTable.h @@ -0,0 +1,69 @@ +//===-- Symtab.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#ifndef liblldb_UnwindTable_h +#define liblldb_UnwindTable_h + +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// A class which holds all the FuncUnwinders objects for a given ObjectFile. +// The UnwindTable is populated with FuncUnwinders objects lazily during +// the debug session. + +class UnwindTable +{ +public: + UnwindTable(ObjectFile& objfile); + ~UnwindTable(); + + lldb_private::DWARFCallFrameInfo * + GetEHFrameInfo (); + + lldb::FuncUnwindersSP + GetFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc); + +// Normally when we create a new FuncUnwinders object we track it in this UnwindTable so it can +// be reused later. But for the target modules show-unwind we want to create brand new +// UnwindPlans for the function of interest - so ignore any existing FuncUnwinders for that +// function and don't add this new one to our UnwindTable. +// This FuncUnwinders object does have a reference to the UnwindTable but the lifetime of this +// uncached FuncUnwinders is expected to be short so in practice this will not be a problem. + lldb::FuncUnwindersSP + GetUncachedFuncUnwindersContainingAddress (const Address& addr, SymbolContext &sc); + +private: + void + Dump (Stream &s); + + void Initialize (); + + typedef std::map collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + ObjectFile& m_object_file; + collection m_unwinds; + + bool m_initialized; // delay some initialization until ObjectFile is set up + + UnwindAssembly* m_assembly_profiler; + + DWARFCallFrameInfo* m_eh_frame; + + DISALLOW_COPY_AND_ASSIGN (UnwindTable); +}; + +} // namespace lldb_private + +#endif // liblldb_UnwindTable_h diff --git a/include/lldb/Symbol/Variable.h b/include/lldb/Symbol/Variable.h new file mode 100644 index 00000000000..07295d090ee --- /dev/null +++ b/include/lldb/Symbol/Variable.h @@ -0,0 +1,184 @@ +//===-- Variable.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Variable_h_ +#define liblldb_Variable_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/UserID.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Symbol/Declaration.h" + +namespace lldb_private { + +class Variable : public UserID +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + Variable (lldb::user_id_t uid, + const char *name, + const char *mangled, // The mangled variable name for variables in namespaces + const lldb::SymbolFileTypeSP &symfile_type_sp, + lldb::ValueType scope, + SymbolContextScope *owner_scope, + Declaration* decl, + const DWARFExpression& location, + bool external, + bool artificial); + + virtual + ~Variable(); + + void + Dump(Stream *s, bool show_context) const; + + bool + DumpDeclaration (Stream *s, + bool show_fullpaths, + bool show_module); + + const Declaration& + GetDeclaration() const + { + return m_declaration; + } + + const ConstString& + GetName() const; + + SymbolContextScope * + GetSymbolContextScope() const + { + return m_owner_scope; + } + + // Since a variable can have a basename "i" and also a mangled + // named "_ZN12_GLOBAL__N_11iE" and a demangled mangled name + // "(anonymous namespace)::i", this function will allow a generic match + // function that can be called by commands and expression parsers to make + // sure we match anything we come across. + bool + NameMatches (const ConstString &name) const + { + if (m_name == name) + return true; + return m_mangled.NameMatches (name); + } + + bool + NameMatches (const RegularExpression& regex) const; + + Type * + GetType(); + + lldb::ValueType + GetScope() const + { + return m_scope; + } + + bool + IsExternal() const + { + return m_external; + } + + bool + IsArtificial() const + { + return m_artificial; + } + + DWARFExpression & + LocationExpression() + { + return m_location; + } + + const DWARFExpression & + LocationExpression() const + { + return m_location; + } + + bool + DumpLocationForAddress (Stream *s, + const Address &address); + + size_t + MemorySize() const; + + void + CalculateSymbolContext (SymbolContext *sc); + + bool + IsInScope (StackFrame *frame); + + bool + LocationIsValidForFrame (StackFrame *frame); + + bool + LocationIsValidForAddress (const Address &address); + + bool + GetLocationIsConstantValueData () const + { + return m_loc_is_const_data; + } + + void + SetLocationIsConstantValueData (bool b) + { + m_loc_is_const_data = b; + } + + typedef size_t (*GetVariableCallback) (void *baton, + const char *name, + VariableList &var_list); + + + static Error + GetValuesForVariableExpressionPath (const char *variable_expr_path, + ExecutionContextScope *scope, + GetVariableCallback callback, + void *baton, + VariableList &variable_list, + ValueObjectList &valobj_list); + + static size_t + AutoComplete (const ExecutionContext &exe_ctx, + const char *name, + StringList &matches, + bool &word_complete); + +protected: + ConstString m_name; // The basename of the variable (no namespaces) + Mangled m_mangled; // The mangled name of the variable + lldb::SymbolFileTypeSP m_symfile_type_sp; // The type pointer of the variable (int, struct, class, etc) + lldb::ValueType m_scope; // global, parameter, local + SymbolContextScope *m_owner_scope; // The symbol file scope that this variable was defined in + Declaration m_declaration; // Declaration location for this item. + DWARFExpression m_location; // The location of this variable that can be fed to DWARFExpression::Evaluate() + uint8_t m_external:1, // Visible outside the containing compile unit? + m_artificial:1, // Non-zero if the variable is not explicitly declared in source + m_loc_is_const_data:1; // The m_location expression contains the constant variable value data, not a DWARF location +private: + Variable(const Variable& rhs); + Variable& operator=(const Variable& rhs); +}; + +} // namespace lldb_private + +#endif // liblldb_Variable_h_ diff --git a/include/lldb/Symbol/VariableList.h b/include/lldb/Symbol/VariableList.h new file mode 100644 index 00000000000..2ce6146f462 --- /dev/null +++ b/include/lldb/Symbol/VariableList.h @@ -0,0 +1,95 @@ +//===-- VariableList.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_VariableList_h_ +#define liblldb_VariableList_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" + +namespace lldb_private { + +class VariableList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ +// VariableList(const SymbolContext &symbol_context); + VariableList(); + virtual ~VariableList(); + + void + AddVariable (const lldb::VariableSP &var_sp); + + bool + AddVariableIfUnique (const lldb::VariableSP &var_sp); + + void + AddVariables (VariableList *variable_list); + + void + Clear(); + + void + Dump(Stream *s, bool show_context) const; + + lldb::VariableSP + GetVariableAtIndex(size_t idx) const; + + lldb::VariableSP + RemoveVariableAtIndex (size_t idx); + + lldb::VariableSP + FindVariable (const ConstString& name); + + uint32_t + FindVariableIndex (const lldb::VariableSP &var_sp); + + // Returns the actual number of unique variables that were added to the + // list. "total_matches" will get updated with the actualy number of + // matches that were found regardless of whether they were unique or not + // to allow for error conditions when nothing is found, versus conditions + // where any varaibles that match "regex" were already in "var_list". + size_t + AppendVariablesIfUnique (const RegularExpression& regex, + VariableList &var_list, + size_t& total_matches); + + size_t + AppendVariablesWithScope (lldb::ValueType type, + VariableList &var_list, + bool if_unique = true); + + uint32_t + FindIndexForVariable (Variable* variable); + + size_t + MemorySize() const; + + size_t + GetSize() const; + +protected: + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + collection m_variables; +private: + //------------------------------------------------------------------ + // For VariableList only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (VariableList); +}; + +} // namespace lldb_private + +#endif // liblldb_VariableList_h_ diff --git a/include/lldb/Symbol/VerifyDecl.h b/include/lldb/Symbol/VerifyDecl.h new file mode 100644 index 00000000000..228e635652e --- /dev/null +++ b/include/lldb/Symbol/VerifyDecl.h @@ -0,0 +1,20 @@ +//===-- VerifyDecl.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_VariableList_h_ +#define lldb_VariableList_h_ + +#include "lldb/Core/ClangForward.h" + +namespace lldb_private +{ + void VerifyDecl (clang::Decl *decl); +} + +#endif diff --git a/include/lldb/Target/ABI.h b/include/lldb/Target/ABI.h new file mode 100644 index 00000000000..16f8ee7fc7d --- /dev/null +++ b/include/lldb/Target/ABI.h @@ -0,0 +1,137 @@ +//===-- ABI.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABI_h_ +#define liblldb_ABI_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class ABI : + public PluginInterface +{ +public: + virtual + ~ABI(); + + virtual size_t + GetRedZoneSize () const = 0; + + virtual bool + PrepareTrivialCall (Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const = 0; + + virtual bool + GetArgumentValues (Thread &thread, + ValueList &values) const = 0; + + lldb::ValueObjectSP + GetReturnValueObject (Thread &thread, + ClangASTType &type, + bool persistent = true) const; + + // Set the Return value object in the current frame as though a function with + virtual Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value) = 0; + +protected: + // This is the method the ABI will call to actually calculate the return value. + // Don't put it in a persistant value object, that will be done by the ABI::GetReturnValueObject. + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (Thread &thread, + ClangASTType &type) const = 0; +public: + virtual bool + CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) = 0; + + virtual bool + CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) = 0; + + virtual bool + RegisterIsVolatile (const RegisterInfo *reg_info) = 0; + + // Should return true if your ABI uses frames when doing stack backtraces. This + // means a frame pointer is used that points to the previous stack frame in some + // way or another. + virtual bool + StackUsesFrames () = 0; + + // Should take a look at a call frame address (CFA) which is just the stack + // pointer value upon entry to a function. ABIs usually impose alignment + // restrictions (4, 8 or 16 byte aligned), and zero is usually not allowed. + // This function should return true if "cfa" is valid call frame address for + // the ABI, and false otherwise. This is used by the generic stack frame unwinding + // code to help determine when a stack ends. + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) = 0; + + // Validates a possible PC value and returns true if an opcode can be at "pc". + virtual bool + CodeAddressIsValid (lldb::addr_t pc) = 0; + + virtual lldb::addr_t + FixCodeAddress (lldb::addr_t pc) + { + // Some targets might use bits in a code address to indicate + // a mode switch. ARM uses bit zero to signify a code address is + // thumb, so any ARM ABI plug-ins would strip those bits. + return pc; + } + + virtual const RegisterInfo * + GetRegisterInfoArray (uint32_t &count) = 0; + + // Some architectures (e.g. x86) will push the return address on the stack and decrement + // the stack pointer when making a function call. This means that every stack frame will + // have a unique CFA. + // Other architectures (e.g. arm) pass the return address in a register so it is possible + // to have a frame on a backtrace that does not push anything on the stack or change the + // CFA. + virtual bool + FunctionCallsChangeCFA () = 0; + + + bool + GetRegisterInfoByName (const ConstString &name, RegisterInfo &info); + + bool + GetRegisterInfoByKind (lldb::RegisterKind reg_kind, + uint32_t reg_num, + RegisterInfo &info); + + static lldb::ABISP + FindPlugin (const ArchSpec &arch); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ABI can see and modify these + //------------------------------------------------------------------ + ABI(); +private: + DISALLOW_COPY_AND_ASSIGN (ABI); +}; + +} // namespace lldb_private + +#endif // liblldb_ABI_h_ diff --git a/include/lldb/Target/CPPLanguageRuntime.h b/include/lldb/Target/CPPLanguageRuntime.h new file mode 100644 index 00000000000..98a4ab88cb2 --- /dev/null +++ b/include/lldb/Target/CPPLanguageRuntime.h @@ -0,0 +1,158 @@ +//===-- CPPLanguageRuntime.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CPPLanguageRuntime_h_ +#define liblldb_CPPLanguageRuntime_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/PluginInterface.h" +#include "lldb/lldb-private.h" +#include "lldb/Target/LanguageRuntime.h" + +namespace lldb_private { + +class CPPLanguageRuntime : + public LanguageRuntime +{ +public: + + class MethodName + { + public: + enum Type + { + eTypeInvalid, + eTypeUnknownMethod, + eTypeClassMethod, + eTypeInstanceMethod + }; + + MethodName () : + m_full(), + m_basename(), + m_context(), + m_arguments(), + m_qualifiers(), + m_type (eTypeInvalid), + m_parsed (false), + m_parse_error (false) + { + } + + MethodName (const ConstString &s) : + m_full(s), + m_basename(), + m_context(), + m_arguments(), + m_qualifiers(), + m_type (eTypeInvalid), + m_parsed (false), + m_parse_error (false) + { + } + + void + Clear(); + + bool + IsValid () const + { + if (m_parse_error) + return false; + if (m_type == eTypeInvalid) + return false; + return (bool)m_full; + } + + Type + GetType () const + { + return m_type; + } + + const ConstString & + GetFullName () const + { + return m_full; + } + + llvm::StringRef + GetBasename (); + + llvm::StringRef + GetContext (); + + llvm::StringRef + GetArguments (); + + llvm::StringRef + GetQualifiers (); + + protected: + void + Parse(); + + 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" + Type m_type; + bool m_parsed; + bool m_parse_error; + }; + + virtual + ~CPPLanguageRuntime(); + + virtual lldb::LanguageType + GetLanguageType () const + { + return lldb::eLanguageTypeC_plus_plus; + } + + virtual bool + IsVTableName (const char *name) = 0; + + virtual bool + GetObjectDescription (Stream &str, ValueObject &object); + + virtual bool + GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope); + + static bool + IsCPPMangledName(const char *name); + + static bool + StripNamespacesFromVariableName (const char *name, const char *&base_name_start, const char *&base_name_end); + + // in some cases, compilers will output different names for one same type. when tht happens, it might be impossible + // to construct SBType objects for a valid type, because the name that is available is not the same as the name that + // can be used as a search key in FindTypes(). the equivalents map here is meant to return possible alternative names + // for a type through which a search can be conducted. Currently, this is only enabled for C++ but can be extended + // to ObjC or other languages if necessary + static uint32_t + FindEquivalentNames(ConstString type_name, std::vector& equivalents); + +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/include/lldb/Target/DynamicLoader.h b/include/lldb/Target/DynamicLoader.h new file mode 100644 index 00000000000..6b76e5891ae --- /dev/null +++ b/include/lldb/Target/DynamicLoader.h @@ -0,0 +1,239 @@ +//===-- DynamicLoader.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoader_h_ +#define liblldb_DynamicLoader_h_ + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class DynamicLoader DynamicLoader.h "lldb/Target/DynamicLoader.h" +/// @brief A plug-in interface definition class for dynamic loaders. +/// +/// Dynamic loader plug-ins track image (shared library) loading and +/// unloading. The class is initialized given a live process that is +/// halted at its entry point or just after attaching. +/// +/// Dynamic loader plug-ins can track the process by registering +/// callbacks using the: +/// Process::RegisterNotificationCallbacks (const Notifications&) +/// function. +/// +/// Breakpoints can also be set in the process which can register +/// functions that get called using: +/// Process::BreakpointSetCallback (lldb::user_id_t, BreakpointHitCallback, void *). +/// These breakpoint callbacks return a boolean value that indicates if +/// the process should continue or halt and should return the global +/// setting for this using: +/// DynamicLoader::StopWhenImagesChange() const. +//---------------------------------------------------------------------- +class DynamicLoader : + public PluginInterface +{ +public: + //------------------------------------------------------------------ + /// Find a dynamic loader plugin for a given process. + /// + /// Scans the installed DynamicLoader plug-ins and tries to find + /// an instance that can be used to track image changes in \a + /// process. + /// + /// @param[in] process + /// The process for which to try and locate a dynamic loader + /// plug-in instance. + /// + /// @param[in] plugin_name + /// An optional name of a specific dynamic loader plug-in that + /// should be used. If NULL, pick the best plug-in. + //------------------------------------------------------------------ + static DynamicLoader* + FindPlugin (Process *process, const char *plugin_name); + + //------------------------------------------------------------------ + /// Construct with a process. + //------------------------------------------------------------------ + DynamicLoader (Process *process); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~DynamicLoader (); + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach () = 0; + + //------------------------------------------------------------------ + /// Called after launching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// the process has stopped for the first time on launch. + //------------------------------------------------------------------ + virtual void + DidLaunch () = 0; + + + //------------------------------------------------------------------ + /// Helper function that can be used to detect when a process has + /// called exec and is now a new and different process. This can + /// be called when necessary to try and detect the exec. The process + /// might be able to answer this question, but sometimes it might + /// not be able and the dynamic loader often knows what the program + /// entry point is. So the process and the dynamic loader can work + /// together to detect this. + //------------------------------------------------------------------ + virtual bool + ProcessDidExec () + { + return false; + } + //------------------------------------------------------------------ + /// Get whether the process should stop when images change. + /// + /// When images (executables and shared libraries) get loaded or + /// unloaded, often debug sessions will want to try and resolve or + /// unresolve breakpoints that are set in these images. Any + /// breakpoints set by DynamicLoader plug-in instances should + /// return this value to ensure consistent debug session behaviour. + /// + /// @return + /// Returns \b true if the process should stop when images + /// change, \b false if the process should resume. + //------------------------------------------------------------------ + bool + GetStopWhenImagesChange () const; + + //------------------------------------------------------------------ + /// Set whether the process should stop when images change. + /// + /// When images (executables and shared libraries) get loaded or + /// unloaded, often debug sessions will want to try and resolve or + /// unresolve breakpoints that are set in these images. The default + /// is set so that the process stops when images change, but this + /// can be overridden using this function callback. + /// + /// @param[in] stop + /// Boolean value that indicates whether the process should stop + /// when images change. + //------------------------------------------------------------------ + void + SetStopWhenImagesChange (bool stop); + + //------------------------------------------------------------------ + /// Provides a plan to step through the dynamic loader trampoline + /// for the current state of \a thread. + /// + /// + /// @param[in] stop_others + /// Whether the plan should be set to stop other threads. + /// + /// @return + /// A pointer to the plan (caller owned) or NULL if we are not at such + /// a trampoline. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) = 0; + + + //------------------------------------------------------------------ + /// Some dynamic loaders provide features where there are a group of symbols "equivalent to" + /// a given symbol one of which will be chosen when the symbol is bound. If you want to + /// set a breakpoint on one of these symbols, you really need to set it on all the + /// equivalent symbols. + /// + /// + /// @param[in] original_symbol + /// The symbol for which we are finding equivalences. + /// + /// @param[in] module_list + /// The set of modules in which to search. + /// + /// @param[out] equivalent_symbols + /// The equivalent symbol list - any equivalent symbols found are appended to this list. + /// + /// @return + /// Number of equivalent symbols found. + //------------------------------------------------------------------ + virtual size_t + FindEquivalentSymbols (Symbol *original_symbol, ModuleList &module_list, SymbolContextList &equivalent_symbols) + { + return 0; + } + + //------------------------------------------------------------------ + /// Ask if it is ok to try and load or unload an shared library + /// (image). + /// + /// The dynamic loader often knows when it would be ok to try and + /// load or unload a shared library. This function call allows the + /// dynamic loader plug-ins to check any current dyld state to make + /// sure it is an ok time to load a shared library. + /// + /// @return + /// \b true if it is currently ok to try and load a shared + /// library into the process, \b false otherwise. + //------------------------------------------------------------------ + virtual Error + CanLoadImage () = 0; + + //------------------------------------------------------------------ + /// Ask if the eh_frame information for the given SymbolContext should + /// be relied on even when it's the first frame in a stack unwind. + /// + /// The CFI instructions from the eh_frame section are normally only + /// valid at call sites -- places where a program could throw an + /// exception and need to unwind out. But some Modules may be known + /// to the system as having reliable eh_frame information at all call + /// sites. This would be the case if the Module's contents are largely + /// hand-written assembly with hand-written eh_frame information. + /// Normally when unwinding from a function at the beginning of a stack + /// unwind lldb will examine the assembly instructions to understand + /// how the stack frame is set up and where saved registers are stored. + /// But with hand-written assembly this is not reliable enough -- we need + /// to consult those function's hand-written eh_frame information. + /// + /// @return + /// \b True if the symbol context should use eh_frame instructions + /// unconditionally when unwinding from this frame. Else \b false, + /// the normal lldb unwind behavior of only using eh_frame when the + /// function appears in the middle of the stack. + //------------------------------------------------------------------ + virtual bool + AlwaysRelyOnEHUnwindInfo (SymbolContext &sym_ctx) + { + return false; + } + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Process* m_process; ///< The process that this dynamic loader plug-in is tracking. +private: + DISALLOW_COPY_AND_ASSIGN (DynamicLoader); + +}; + +} // namespace lldb_private + +#endif // liblldb_DynamicLoader_h_ diff --git a/include/lldb/Target/ExecutionContext.h b/include/lldb/Target/ExecutionContext.h new file mode 100644 index 00000000000..de5fe14934a --- /dev/null +++ b/include/lldb/Target/ExecutionContext.h @@ -0,0 +1,778 @@ +//===-- ExecutionContext.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// Execution context objects refer to objects in the execution of the +/// program that is being debugged. The consist of one or more of the +/// following objects: target, process, thread, and frame. Many objects +/// in the debugger need to track different executions contexts. For +/// example, a local function variable might have an execution context +/// that refers to a stack frame. A global or static variable might +/// refer to a target since a stack frame isn't required in order to +/// evaluate a global or static variable (a process isn't necessarily +/// needed for a global variable since we might be able to read the +/// variable value from a data section in one of the object files in +/// a target). There are two types of objects that hold onto execution +/// contexts: ExecutionContextRef and ExecutionContext. Both of these +/// objects are deascribed below. +/// +/// Not all objects in an ExectionContext objects will be valid. If you want +/// to refer stronly (ExectionContext) or weakly (ExectionContextRef) to +/// a process, then only the process and target references will be valid. +/// For threads, only the thread, process and target references will be +/// filled in. For frames, all of the objects will be filled in. +/// +/// These classes are designed to be used as baton objects that get passed +/// to a wide variety of functions that require execution contexts. +//===----------------------------------------------------------------------===// + + + +#ifndef liblldb_ExecutionContext_h_ +#define liblldb_ExecutionContext_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Target/StackID.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ExecutionContextRef ExecutionContext.h "lldb/Target/ExecutionContext.h" +/// @brief A class that holds a weak reference to an execution context. +/// +/// ExecutionContextRef objects are designed to hold onto an execution +/// context that might change over time. For example, if an object wants +/// to refer to a stack frame, it should hold onto an ExecutionContextRef +/// to a frame object. The backing object that represents the stack frame +/// might change over time and instaces of this object can track the logical +/// object that refers to a frame even if it does change. +/// +/// These objects also don't keep execution objects around longer than they +/// should since they use weak pointers. For example if an object refers +/// to a stack frame and a stack frame is no longer in a thread, then a +/// ExecutionContextRef object that refers to that frame will not be able +/// to get a shared pointer to those objects since they are no longer around. +/// +/// ExecutionContextRef objects can also be used as objects in classes +/// that want to track a "previous execution context". Since the weak +/// references to the execution objects (target, process, thread and frame) +/// don't keep these objects around, they are safe to keep around. +/// +/// The general rule of thumb is all long lived objects that want to +/// refer to execution contexts should use ExecutionContextRef objcts. +/// The ExecutionContext class is used to temporarily get shared +/// pointers to any execution context objects that are still around +/// so they are guaranteed to exist during a function that requires the +/// objects. ExecutionContext objects should NOT be used for long term +/// storage since they will keep objects alive with extra shared pointer +/// references to these objects. +//---------------------------------------------------------------------- +class ExecutionContextRef +{ +public: + //------------------------------------------------------------------ + /// Default Constructor. + //------------------------------------------------------------------ + ExecutionContextRef(); + + //------------------------------------------------------------------ + /// Copy Constructor. + //------------------------------------------------------------------ + ExecutionContextRef (const ExecutionContextRef &rhs); + + //------------------------------------------------------------------ + /// Construct using an ExecutionContext object that might be NULL. + /// + /// If \a exe_ctx_ptr is valid, then make weak references to any + /// valid objects in the ExecutionContext, othewise no weak + /// references to any execution context objects will be made. + //------------------------------------------------------------------ + ExecutionContextRef (const ExecutionContext *exe_ctx_ptr); + + //------------------------------------------------------------------ + /// Construct using an ExecutionContext object. + /// + /// Make weak references to any valid objects in the ExecutionContext. + //------------------------------------------------------------------ + ExecutionContextRef (const ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Assignment operator + /// + /// Copy all weak refernces in \a rhs. + //------------------------------------------------------------------ + ExecutionContextRef & + operator =(const ExecutionContextRef &rhs); + + //------------------------------------------------------------------ + /// Assignment operator from a ExecutionContext + /// + /// Make weak refernces to any stringly referenced objects in \a exe_ctx. + //------------------------------------------------------------------ + ExecutionContextRef & + operator =(const ExecutionContext &exe_ctx); + + //------------------------------------------------------------------ + /// Construct using the target and all the selected items inside of it + /// (the process and its selected thread, and the thread's selected + /// frame). If there is no selected thread, default to the first thread + /// If there is no selected frame, default to the first frame. + //------------------------------------------------------------------ + ExecutionContextRef (Target *target, bool adopt_selected); + + //------------------------------------------------------------------ + /// Construct using an execution context scope. + /// + /// If the ExecutionContextScope object is valid and refers to a frame, + /// make weak refernces too the frame, thread, process and target. + /// If the ExecutionContextScope object is valid and refers to a thread, + /// make weak refernces too the thread, process and target. + /// If the ExecutionContextScope object is valid and refers to a process, + /// make weak refernces too the process and target. + /// If the ExecutionContextScope object is valid and refers to a target, + /// make weak refernces too the target. + //------------------------------------------------------------------ + ExecutionContextRef (ExecutionContextScope *exe_scope); + + //------------------------------------------------------------------ + /// Construct using an execution context scope. + /// + /// If the ExecutionContextScope object refers to a frame, + /// make weak refernces too the frame, thread, process and target. + /// If the ExecutionContextScope object refers to a thread, + /// make weak refernces too the thread, process and target. + /// If the ExecutionContextScope object refers to a process, + /// make weak refernces too the process and target. + /// If the ExecutionContextScope object refers to a target, + /// make weak refernces too the target. + //------------------------------------------------------------------ + ExecutionContextRef (ExecutionContextScope &exe_scope); + + ~ExecutionContextRef(); + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the process and thread to NULL, and the frame index to an + /// invalid value. + //------------------------------------------------------------------ + void + Clear (); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the target + /// referenced in \a target_sp. + /// + /// If \a target_sp is valid this object will create a weak + /// reference to that object, otherwise any previous target weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the target will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the target's process, use + /// the ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::TargetSP &, bool) + //------------------------------------------------------------------ + void + SetTargetSP (const lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the process + /// referenced in \a process_sp. + /// + /// If \a process_sp is valid this object will create a weak + /// reference to that object, otherwise any previous process weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the process will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the target, use the + /// ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::ProcessSP &) + //------------------------------------------------------------------ + void + SetProcessSP (const lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the thread + /// referenced in \a thread_sp. + /// + /// If \a thread_sp is valid this object will create a weak + /// reference to that object, otherwise any previous thread weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the thread will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the thread's process and + /// target, use the ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::ThreadSP &) + //------------------------------------------------------------------ + void + SetThreadSP (const lldb::ThreadSP &thread_sp); + + //------------------------------------------------------------------ + /// Set accessor that creates a weak reference to the frame + /// referenced in \a frame_sp. + /// + /// If \a frame_sp is valid this object will create a weak + /// reference to that object, otherwise any previous frame weak + /// reference contained in this object will be reset. + /// + /// Only the weak reference to the frame will be updated, no other + /// weak references will be modified. If you want this execution + /// context to make a weak reference to the frame's thread, process + /// and target, use the ExecutionContextRef::SetContext() functions. + /// + /// @see ExecutionContextRef::SetContext(const lldb::StackFrameSP &) + //------------------------------------------------------------------ + void + SetFrameSP (const lldb::StackFrameSP &frame_sp); + + void + SetTargetPtr (Target* target, bool adopt_selected); + + void + SetProcessPtr (Process *process); + + void + SetThreadPtr (Thread *thread); + + void + SetFramePtr (StackFrame *frame); + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak target + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a target that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::TargetSP + GetTargetSP () const; + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak process + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a process that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::ProcessSP + GetProcessSP () const; + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak thread + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a thread that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::ThreadSP + GetThreadSP () const; + + //------------------------------------------------------------------ + /// Get accessor that creates a strong reference from the weak frame + /// reference contained in this object. + /// + /// @returns + /// A shared pointer to a frame that is not guaranteed to be valid. + //------------------------------------------------------------------ + lldb::StackFrameSP + GetFrameSP () const; + + //------------------------------------------------------------------ + /// Create an ExecutionContext object from this object. + /// + /// Create strong references to any execution context objects that + /// are still valid. Any of the returned shared pointers in the + /// ExecutionContext objects is not guaranteed to be valid. + /// @returns + /// An execution context object that has strong references to + /// any valid weak references in this object. + //------------------------------------------------------------------ + ExecutionContext + Lock () const; + + //------------------------------------------------------------------ + /// Returns true if this object has a weak reference to a thread. + /// The return value is only an indication of wether this object has + /// a weak reference and does not indicate wether the weak rerference + /// is valid or not. + //------------------------------------------------------------------ + bool + HasThreadRef () const + { + return m_tid != LLDB_INVALID_THREAD_ID; + } + + //------------------------------------------------------------------ + /// Returns true if this object has a weak reference to a frame. + /// The return value is only an indication of wether this object has + /// a weak reference and does not indicate wether the weak rerference + /// is valid or not. + //------------------------------------------------------------------ + bool + HasFrameRef () const + { + return m_stack_id.IsValid(); + } + + void + ClearThread () + { + m_thread_wp.reset(); + m_tid = LLDB_INVALID_THREAD_ID; + } + + void + ClearFrame () + { + m_stack_id.Clear(); + } + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::TargetWP m_target_wp; ///< A weak reference to a target + lldb::ProcessWP m_process_wp; ///< A weak reference to a process + mutable lldb::ThreadWP m_thread_wp; ///< A weak reference to a thread + lldb::tid_t m_tid; ///< The thread ID that this object refers to in case the backing object changes + StackID m_stack_id; ///< The stack ID that this object refers to in case the backing object changes +}; + +//---------------------------------------------------------------------- +/// @class ExecutionContext ExecutionContext.h "lldb/Target/ExecutionContext.h" +/// @brief A class that contains an execution context. +/// +/// This baton object can be passed into any function that requires +/// a context that specifies a target, process, thread and frame. +/// These objects are designed to be used for short term execution +/// context object storage while a function might be trying to evaluate +/// something that requires a thread or frame. ExecutionContextRef +/// objects can be used to initialize one of these objects to turn +/// the weak execution context object references to the target, process, +/// thread and frame into strong references (shared pointers) so that +/// functions can guarantee that these objects won't go away in the +/// middle of a function. +/// +/// ExecutionContext objects should be used as short lived objects +/// (typically on the stack) in order to lock down an execution context +/// for local use and for passing down to other functions that also +/// require specific contexts. They should NOT be used for long term +/// storage, for long term storage use ExecutionContextRef objects. +//---------------------------------------------------------------------- +class ExecutionContext +{ +public: + //------------------------------------------------------------------ + /// Default Constructor. + //------------------------------------------------------------------ + ExecutionContext(); + + //------------------------------------------------------------------ + // Copy constructor + //------------------------------------------------------------------ + ExecutionContext (const ExecutionContext &rhs); + + //------------------------------------------------------------------ + // Adopt the target and optionally its current context. + //------------------------------------------------------------------ + ExecutionContext (Target* t, bool fill_current_process_thread_frame = true); + + //------------------------------------------------------------------ + // Create execution contexts from shared pointers + //------------------------------------------------------------------ + ExecutionContext (const lldb::TargetSP &target_sp, bool get_process); + ExecutionContext (const lldb::ProcessSP &process_sp); + ExecutionContext (const lldb::ThreadSP &thread_sp); + ExecutionContext (const lldb::StackFrameSP &frame_sp); + //------------------------------------------------------------------ + // Create execution contexts from weak pointers + //------------------------------------------------------------------ + ExecutionContext (const lldb::TargetWP &target_wp, bool get_process); + ExecutionContext (const lldb::ProcessWP &process_wp); + ExecutionContext (const lldb::ThreadWP &thread_wp); + ExecutionContext (const lldb::StackFrameWP &frame_wp); + ExecutionContext (const ExecutionContextRef &exe_ctx_ref); + ExecutionContext (const ExecutionContextRef *exe_ctx_ref); + + // These two variants take in a locker, and grab the target, lock the API mutex into locker, then + // fill in the rest of the shared pointers. + ExecutionContext (const ExecutionContextRef &exe_ctx_ref, Mutex::Locker &locker); + ExecutionContext (const ExecutionContextRef *exe_ctx_ref, Mutex::Locker &locker); + //------------------------------------------------------------------ + // Create execution contexts from execution context scopes + //------------------------------------------------------------------ + ExecutionContext (ExecutionContextScope *exe_scope); + ExecutionContext (ExecutionContextScope &exe_scope); + + + ExecutionContext & + operator =(const ExecutionContext &rhs); + + bool + operator ==(const ExecutionContext &rhs) const; + + bool + operator !=(const ExecutionContext &rhs) const; + + //------------------------------------------------------------------ + /// Construct with process, thread, and frame index. + /// + /// Initialize with process \a p, thread \a t, and frame index \a f. + /// + /// @param[in] process + /// The process for this execution context. + /// + /// @param[in] thread + /// The thread for this execution context. + /// + /// @param[in] frame + /// The frame index for this execution context. + //------------------------------------------------------------------ + ExecutionContext (Process* process, + Thread *thread = NULL, + StackFrame * frame = NULL); + + + ~ExecutionContext(); + //------------------------------------------------------------------ + /// Clear the object's state. + /// + /// Sets the process and thread to NULL, and the frame index to an + /// invalid value. + //------------------------------------------------------------------ + void + Clear (); + + RegisterContext * + GetRegisterContext () const; + + ExecutionContextScope * + GetBestExecutionContextScope () const; + + uint32_t + GetAddressByteSize() const; + + //------------------------------------------------------------------ + /// Returns a pointer to the target object. + /// + /// The returned pointer might be NULL. Calling HasTargetScope(), + /// HasProcessScope(), HasThreadScope(), or HasFrameScope() + /// can help to pre-validate this pointer so that this accessor can + /// freely be used without having to check for NULL each time. + /// + /// @see ExecutionContext::HasTargetScope() const + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Target * + GetTargetPtr () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the process object. + /// + /// The returned pointer might be NULL. Calling HasProcessScope(), + /// HasThreadScope(), or HasFrameScope() can help to pre-validate + /// this pointer so that this accessor can freely be used without + /// having to check for NULL each time. + /// + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Process * + GetProcessPtr () const; + + //------------------------------------------------------------------ + /// Returns a pointer to the thread object. + /// + /// The returned pointer might be NULL. Calling HasThreadScope() or + /// HasFrameScope() can help to pre-validate this pointer so that + /// this accessor can freely be used without having to check for + /// NULL each time. + /// + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Thread * + GetThreadPtr () const + { + return m_thread_sp.get(); + } + + //------------------------------------------------------------------ + /// Returns a pointer to the frame object. + /// + /// The returned pointer might be NULL. Calling HasFrameScope(), + /// can help to pre-validate this pointer so that this accessor can + /// freely be used without having to check for NULL each time. + /// + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + StackFrame * + GetFramePtr () const + { + return m_frame_sp.get(); + } + + //------------------------------------------------------------------ + /// Returns a reference to the target object. + /// + /// Clients should call HasTargetScope(), HasProcessScope(), + /// HasThreadScope(), or HasFrameScope() prior to calling this + /// function to ensure that this ExecutionContext object contains + /// a valid target. + /// + /// @see ExecutionContext::HasTargetScope() const + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Target & + GetTargetRef () const; + + //------------------------------------------------------------------ + /// Returns a reference to the process object. + /// + /// Clients should call HasProcessScope(), HasThreadScope(), or + /// HasFrameScope() prior to calling this function to ensure that + /// this ExecutionContext object contains a valid target. + /// + /// @see ExecutionContext::HasProcessScope() const + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Process & + GetProcessRef () const; + + //------------------------------------------------------------------ + /// Returns a reference to the thread object. + /// + /// Clients should call HasThreadScope(), or HasFrameScope() prior + /// to calling this function to ensure that this ExecutionContext + /// object contains a valid target. + /// + /// @see ExecutionContext::HasThreadScope() const + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + Thread & + GetThreadRef () const; + + //------------------------------------------------------------------ + /// Returns a reference to the thread object. + /// + /// Clients should call HasFrameScope() prior to calling this + /// function to ensure that this ExecutionContext object contains + /// a valid target. + /// + /// @see ExecutionContext::HasFrameScope() const + //------------------------------------------------------------------ + StackFrame & + GetFrameRef () const; + + //------------------------------------------------------------------ + /// Get accessor to get the target shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::TargetSP & + GetTargetSP () const + { + return m_target_sp; + } + + //------------------------------------------------------------------ + /// Get accessor to get the process shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::ProcessSP & + GetProcessSP () const + { + return m_process_sp; + } + + //------------------------------------------------------------------ + /// Get accessor to get the thread shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::ThreadSP & + GetThreadSP () const + { + return m_thread_sp; + } + + //------------------------------------------------------------------ + /// Get accessor to get the frame shared pointer. + /// + /// The returned shared pointer is not guaranteed to be valid. + //------------------------------------------------------------------ + const lldb::StackFrameSP & + GetFrameSP () const + { + return m_frame_sp; + } + + //------------------------------------------------------------------ + /// Set accessor to set only the target shared pointer. + //------------------------------------------------------------------ + void + SetTargetSP (const lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the process shared pointer. + //------------------------------------------------------------------ + void + SetProcessSP (const lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the thread shared pointer. + //------------------------------------------------------------------ + void + SetThreadSP (const lldb::ThreadSP &thread_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the frame shared pointer. + //------------------------------------------------------------------ + void + SetFrameSP (const lldb::StackFrameSP &frame_sp); + + //------------------------------------------------------------------ + /// Set accessor to set only the target shared pointer from a target + /// pointer. + //------------------------------------------------------------------ + void + SetTargetPtr (Target* target); + + //------------------------------------------------------------------ + /// Set accessor to set only the process shared pointer from a + /// process pointer. + //------------------------------------------------------------------ + void + SetProcessPtr (Process *process); + + //------------------------------------------------------------------ + /// Set accessor to set only the thread shared pointer from a thread + /// pointer. + //------------------------------------------------------------------ + void + SetThreadPtr (Thread *thread); + + //------------------------------------------------------------------ + /// Set accessor to set only the frame shared pointer from a frame + /// pointer. + //------------------------------------------------------------------ + void + SetFramePtr (StackFrame *frame); + + //------------------------------------------------------------------ + // Set the execution context using a target shared pointer. + // + // If "target_sp" is valid, sets the target context to match and + // if "get_process" is true, sets the process shared pointer if + // the target currently has a process. + //------------------------------------------------------------------ + void + SetContext (const lldb::TargetSP &target_sp, bool get_process); + + //------------------------------------------------------------------ + // Set the execution context using a process shared pointer. + // + // If "process_sp" is valid, then set the process and target in this + // context. Thread and frame contexts will be cleared. + // If "process_sp" is not valid, all shared pointers are reset. + //------------------------------------------------------------------ + void + SetContext (const lldb::ProcessSP &process_sp); + + //------------------------------------------------------------------ + // Set the execution context using a thread shared pointer. + // + // If "thread_sp" is valid, then set the thread, process and target + // in this context. The frame context will be cleared. + // If "thread_sp" is not valid, all shared pointers are reset. + //------------------------------------------------------------------ + void + SetContext (const lldb::ThreadSP &thread_sp); + + //------------------------------------------------------------------ + // Set the execution context using a frame shared pointer. + // + // If "frame_sp" is valid, then set the frame, thread, process and + // target in this context + // If "frame_sp" is not valid, all shared pointers are reset. + //------------------------------------------------------------------ + void + SetContext (const lldb::StackFrameSP &frame_sp); + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr() and + /// GetTargetRef() do not need to be checked for validity. + //------------------------------------------------------------------ + bool + HasTargetScope () const; + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target and process. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr() and + /// GetTargetRef(), GetProcessPtr(), and GetProcessRef(), do not + /// need to be checked for validity. + //------------------------------------------------------------------ + bool + HasProcessScope () const; + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target, process, and thread. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr(), + /// GetTargetRef(), GetProcessPtr(), GetProcessRef(), GetThreadPtr(), + /// and GetThreadRef() do not need to be checked for validity. + //------------------------------------------------------------------ + bool + HasThreadScope () const; + + //------------------------------------------------------------------ + /// Returns true the ExecutionContext object contains a valid + /// target, process, thread and frame. + /// + /// This function can be called after initializing an ExecutionContext + /// object, and if it returns true, calls to GetTargetPtr(), + /// GetTargetRef(), GetProcessPtr(), GetProcessRef(), GetThreadPtr(), + /// GetThreadRef(), GetFramePtr(), and GetFrameRef() do not need + /// to be checked for validity. + //------------------------------------------------------------------ + bool + HasFrameScope () const; + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + lldb::TargetSP m_target_sp; ///< The target that owns the process/thread/frame + lldb::ProcessSP m_process_sp; ///< The process that owns the thread/frame + lldb::ThreadSP m_thread_sp; ///< The thread that owns the frame + lldb::StackFrameSP m_frame_sp; ///< The stack frame in thread. +}; +} // namespace lldb_private + +#endif // liblldb_ExecutionContext_h_ diff --git a/include/lldb/Target/ExecutionContextScope.h b/include/lldb/Target/ExecutionContextScope.h new file mode 100644 index 00000000000..7ba40971af2 --- /dev/null +++ b/include/lldb/Target/ExecutionContextScope.h @@ -0,0 +1,74 @@ +//===-- ExecutionContextScope.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ExecutionContextScope_h_ +#define liblldb_ExecutionContextScope_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class ExecutionContextScope ExecutionContextScope.h "lldb/Symbol/ExecutionContextScope.h" +/// @brief Inherit from this if your object can reconstruct its +/// execution context. +/// +/// Many objects that have pointers back to parent execution context +/// objects can inherit from this pure virtual class can reconstruct +/// their execution context without having to keep a complete +/// ExecutionContext object in the object state. Examples of these +/// objects include: Process, Thread, RegisterContext and StackFrame. +/// +/// Bbjects can contain a valid pointer to an instance of this so they +/// can reconstruct the execution context. +/// +/// Objects that adhere to this protocol can reconstruct enough of a +/// execution context to allow functions that take a execution contexts +/// to be called. +//---------------------------------------------------------------------- +class ExecutionContextScope +{ +public: + virtual + ~ExecutionContextScope () {} + + virtual lldb::TargetSP + CalculateTarget () = 0; + + virtual lldb::ProcessSP + CalculateProcess () = 0; + + virtual lldb::ThreadSP + CalculateThread () = 0; + + virtual lldb::StackFrameSP + CalculateStackFrame () = 0; + + //------------------------------------------------------------------ + /// Reconstruct the object's execution context into \a sc. + /// + /// The object should fill in as much of the ExecutionContextScope as it + /// can so function calls that require a execution context can be made + /// for the given object. + /// + /// @param[out] exe_ctx + /// A reference to an execution context object that gets filled + /// in. + //------------------------------------------------------------------ + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx) = 0; +}; + +} // namespace lldb_private + +#endif // liblldb_ExecutionContextScope_h_ diff --git a/include/lldb/Target/LanguageRuntime.h b/include/lldb/Target/LanguageRuntime.h new file mode 100644 index 00000000000..93c0437da75 --- /dev/null +++ b/include/lldb/Target/LanguageRuntime.h @@ -0,0 +1,113 @@ +//===-- LanguageRuntime.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_LanguageRuntime_h_ +#define liblldb_LanguageRuntime_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverName.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/Value.h" +#include "lldb/Target/ExecutionContextScope.h" + +namespace lldb_private { + +class LanguageRuntime : + public PluginInterface +{ +public: + virtual + ~LanguageRuntime(); + + static LanguageRuntime* + FindPlugin (Process *process, lldb::LanguageType language); + + virtual lldb::LanguageType + GetLanguageType () const = 0; + + virtual bool + GetObjectDescription (Stream &str, ValueObject &object) = 0; + + virtual bool + GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope) = 0; + + // this call should return true if it could set the name and/or the type + virtual bool + GetDynamicTypeAndAddress (ValueObject &in_value, + lldb::DynamicValueType use_dynamic, + TypeAndOrName &class_type_or_name, + Address &address) = 0; + + // This should be a fast test to determine whether it is likely that this value would + // have a dynamic type. + virtual bool + CouldHaveDynamicValue (ValueObject &in_value) = 0; + + virtual void + SetExceptionBreakpoints () + { + } + + virtual void + ClearExceptionBreakpoints () + { + } + + virtual bool + ExceptionBreakpointsExplainStop (lldb::StopInfoSP stop_reason) + { + return false; + } + + static lldb::BreakpointSP + CreateExceptionBreakpoint (Target &target, + lldb::LanguageType language, + bool catch_bp, + bool throw_bp, + bool is_internal = false); + + static lldb::LanguageType + GetLanguageTypeFromString (const char *string); + + static const char * + GetNameForLanguageType (lldb::LanguageType language); + + Process * + GetProcess() + { + return m_process; + } + + virtual lldb::BreakpointResolverSP + CreateExceptionResolver (Breakpoint *bkpt, bool catch_bp, bool throw_bp) = 0; + + virtual lldb::SearchFilterSP + CreateExceptionSearchFilter (); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from LanguageRuntime can see and modify these + //------------------------------------------------------------------ + + LanguageRuntime(Process *process); + Process *m_process; +private: + DISALLOW_COPY_AND_ASSIGN (LanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_LanguageRuntime_h_ diff --git a/include/lldb/Target/Memory.h b/include/lldb/Target/Memory.h new file mode 100644 index 00000000000..568bbcdf2f7 --- /dev/null +++ b/include/lldb/Target/Memory.h @@ -0,0 +1,196 @@ +//===-- Memory.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Memory_h_ +#define liblldb_Memory_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + //---------------------------------------------------------------------- + // A class to track memory that was read from a live process between + // runs. + //---------------------------------------------------------------------- + class MemoryCache + { + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + MemoryCache (Process &process); + + ~MemoryCache (); + + void + Clear(bool clear_invalid_ranges = false); + + void + Flush (lldb::addr_t addr, size_t size); + + size_t + Read (lldb::addr_t addr, + void *dst, + size_t dst_len, + Error &error); + + uint32_t + GetMemoryCacheLineSize() const + { + return m_cache_line_byte_size ; + } + + void + AddInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size); + + bool + RemoveInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size); + + protected: + typedef std::map BlockMap; + typedef RangeArray InvalidRanges; + //------------------------------------------------------------------ + // Classes that inherit from MemoryCache can see and modify these + //------------------------------------------------------------------ + Process &m_process; + uint32_t m_cache_line_byte_size; + Mutex m_mutex; + BlockMap m_cache; + InvalidRanges m_invalid_ranges; + private: + DISALLOW_COPY_AND_ASSIGN (MemoryCache); + }; + + + class AllocatedBlock + { + public: + AllocatedBlock (lldb::addr_t addr, + uint32_t byte_size, + uint32_t permissions, + uint32_t chunk_size); + + ~AllocatedBlock (); + + lldb::addr_t + ReserveBlock (uint32_t size); + + bool + FreeBlock (lldb::addr_t addr); + + lldb::addr_t + GetBaseAddress () const + { + return m_addr; + } + + uint32_t + GetByteSize () const + { + return m_byte_size; + } + + uint32_t + GetPermissions () const + { + return m_permissions; + } + + uint32_t + GetChunkSize () const + { + return m_chunk_size; + } + + bool + Contains (lldb::addr_t addr) const + { + return ((addr >= m_addr) && addr < (m_addr + m_byte_size)); + } + protected: + uint32_t + TotalChunks () const + { + return m_byte_size / m_chunk_size; + } + + uint32_t + CalculateChunksNeededForSize (uint32_t size) const + { + return (size + m_chunk_size - 1) / m_chunk_size; + } + const lldb::addr_t m_addr; // Base address of this block of memory + const uint32_t m_byte_size; // 4GB of chunk should be enough... + const uint32_t m_permissions; // Permissions for this memory (logical OR of lldb::Permissions bits) + const uint32_t m_chunk_size; // The size of chunks that the memory at m_addr is divied up into + typedef std::map OffsetToChunkSize; + OffsetToChunkSize m_offset_to_chunk_size; + }; + + + //---------------------------------------------------------------------- + // A class that can track allocated memory and give out allocated memory + // without us having to make an allocate/deallocate call every time we + // need some memory in a process that is being debugged. + //---------------------------------------------------------------------- + class AllocatedMemoryCache + { + public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + AllocatedMemoryCache (Process &process); + + ~AllocatedMemoryCache (); + + void + Clear(); + + lldb::addr_t + AllocateMemory (size_t byte_size, + uint32_t permissions, + Error &error); + + bool + DeallocateMemory (lldb::addr_t ptr); + + protected: + typedef std::shared_ptr AllocatedBlockSP; + + AllocatedBlockSP + AllocatePage (uint32_t byte_size, + uint32_t permissions, + uint32_t chunk_size, + Error &error); + + + //------------------------------------------------------------------ + // Classes that inherit from MemoryCache can see and modify these + //------------------------------------------------------------------ + Process &m_process; + Mutex m_mutex; + typedef std::multimap PermissionsToBlockMap; + PermissionsToBlockMap m_memory_map; + + private: + DISALLOW_COPY_AND_ASSIGN (AllocatedMemoryCache); + }; + +} // namespace lldb_private + +#endif // liblldb_Memory_h_ diff --git a/include/lldb/Target/ObjCLanguageRuntime.h b/include/lldb/Target/ObjCLanguageRuntime.h new file mode 100644 index 00000000000..7bac5725644 --- /dev/null +++ b/include/lldb/Target/ObjCLanguageRuntime.h @@ -0,0 +1,604 @@ +//===-- ObjCLanguageRuntime.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjCLanguageRuntime_h_ +#define liblldb_ObjCLanguageRuntime_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/TypeVendor.h" +#include "lldb/Target/LanguageRuntime.h" + +namespace lldb_private { + +class ClangUtilityFunction; + +class ObjCLanguageRuntime : + public LanguageRuntime +{ +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); + } + + 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 (bool)GetCategory(); + } + + Type + GetType () const + { + return m_type; + } + + const ConstString & + GetFullName () const + { + return m_full; + } + + ConstString + GetFullNameWithoutCategory (bool empty_if_no_category); + + bool + SetName (const char *name, bool strict); + + const ConstString & + GetClassName (); + + const ConstString & + GetClassNameWithCategory (); + + const ConstString & + GetCategory (); + + const ConstString & + GetSelector (); + + // Get all possible names for a method. Examples: + // If name is "+[NSString(my_additions) myStringWithCString:]" + // names[0] => "+[NSString(my_additions) myStringWithCString:]" + // names[1] => "+[NSString myStringWithCString:]" + // If name is specified without the leading '+' or '-' like "[NSString(my_additions) myStringWithCString:]" + // names[0] => "+[NSString(my_additions) myStringWithCString:]" + // names[1] => "-[NSString(my_additions) myStringWithCString:]" + // names[2] => "+[NSString myStringWithCString:]" + // names[3] => "-[NSString myStringWithCString:]" + size_t + GetFullNames (std::vector &names, bool append); + 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; + + }; + typedef lldb::addr_t ObjCISA; + + class ClassDescriptor; + typedef std::shared_ptr 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 () + { + } + + virtual ConstString + GetClassName () = 0; + + virtual ClassDescriptorSP + GetSuperclass () = 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 = NULL, + uint64_t* value_bits = NULL, + uint64_t* payload = NULL) = 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 const &superclass_func, + std::function const &instance_method_func, + std::function const &class_method_func, + std::function const &ivar_func) + { + return false; + } + + lldb::TypeSP + GetType () + { + return m_type_wp.lock(); + } + + void + SetType (const lldb::TypeSP &type_sp) + { + m_type_wp = type_sp; + } + + 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; + }; + + virtual ClassDescriptorSP + GetClassDescriptor (ValueObject& in_value); + + ClassDescriptorSP + GetNonKVOClassDescriptor (ValueObject& in_value); + + virtual ClassDescriptorSP + GetClassDescriptorFromClassName (const ConstString &class_name); + + virtual ClassDescriptorSP + GetClassDescriptorFromISA (ObjCISA isa); + + ClassDescriptorSP + GetNonKVOClassDescriptor (ObjCISA isa); + + virtual + ~ObjCLanguageRuntime(); + + virtual lldb::LanguageType + GetLanguageType () const + { + 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; + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) = 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); + + virtual ClangUtilityFunction * + CreateObjectChecker (const char *) = 0; + + virtual ObjCRuntimeVersions + GetRuntimeVersion () + { + return 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(const ConstString &name); + + virtual ConstString + GetActualTypeName(ObjCISA isa); + + virtual ObjCISA + GetParentClass(ObjCISA isa); + + virtual TypeVendor * + GetTypeVendor() + { + return NULL; + } + + // 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 (ClangASTType &parent_qual_type, const char *ivar_name); + + // Given the name of an Objective-C runtime symbol (e.g., ivar offset symbol), + // try to determine from the runtime what the value of that symbol would be. + // Useful when the underlying binary is stripped. + virtual lldb::addr_t + LookupRuntimeSymbol (const ConstString &name) + { + return LLDB_INVALID_ADDRESS; + } + + //------------------------------------------------------------------ + /// Chop up an objective C function prototype. + /// + /// Chop up an objective C function fullname and optionally fill in + /// any non-NULL ConstString objects. If a ConstString * is NULL, + /// then this name doesn't get filled in + /// + /// @param[in] name + /// A fully specified objective C function name. The string might + /// contain a category and it includes the leading "+" or "-" and + /// the square brackets, no types for the arguments, just the plain + /// selector. A few examples: + /// "-[NSStringDrawingContext init]" + /// "-[NSStringDrawingContext addString:inRect:]" + /// "-[NSString(NSStringDrawing) sizeWithAttributes:]" + /// "+[NSString(NSStringDrawing) usesFontLeading]" + /// + /// @param[out] class_name + /// If non-NULL, this string will be filled in with the class + /// name including the category. The examples above would return: + /// "NSStringDrawingContext" + /// "NSStringDrawingContext" + /// "NSString(NSStringDrawing)" + /// "NSString(NSStringDrawing)" + /// + /// @param[out] selector_name + /// If non-NULL, this string will be filled in with the selector + /// name. The examples above would return: + /// "init" + /// "addString:inRect:" + /// "sizeWithAttributes:" + /// "usesFontLeading" + /// + /// @param[out] name_sans_category + /// If non-NULL, this string will be filled in with the class + /// name _without_ the category. If there is no category, and empty + /// string will be returned (as the result would be normally returned + /// in the "class_name" argument). The examples above would return: + /// + /// + /// "-[NSString sizeWithAttributes:]" + /// "+[NSString usesFontLeading]" + /// + /// @param[out] class_name_sans_category + /// If non-NULL, this string will be filled in with the prototype + /// name _without_ the category. If there is no category, and empty + /// string will be returned (as this is already the value that was + /// passed in). The examples above would return: + /// + /// + /// "NSString" + /// "NSString" + /// + /// @return + /// Returns the number of strings that were successfully filled + /// in. + //------------------------------------------------------------------ +// static uint32_t +// ParseMethodName (const char *name, +// ConstString *class_name, // Class name (with category if there is one) +// ConstString *selector_name, // selector only +// ConstString *name_sans_category, // full function name with no category (empty if no category) +// ConstString *class_name_sans_category);// Class name without category (empty if no category) + + 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, ':') == NULL) + return true; + else if (name[strlen(name) - 1] == ':') + return true; + else + return false; + } + + 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); + } + + virtual void + SymbolsDidLoad (const ModuleList& module_list) + { + m_negative_complete_class_cache.clear(); + } + +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 ->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 MsgImplMap; + typedef std::map ISAToDescriptorMap; + typedef std::multimap HashToISAMap; + typedef ISAToDescriptorMap::iterator ISAToDescriptorIterator; + typedef HashToISAMap::iterator HashToISAIterator; + + MsgImplMap m_impl_cache; + LazyBool m_has_new_literals_and_indexing; + ISAToDescriptorMap m_isa_to_descriptor; + HashToISAMap m_hash_to_isa_map; + +protected: + uint32_t m_isa_to_descriptor_stop_id; + + typedef std::map CompleteClassMap; + CompleteClassMap m_complete_class_cache; + + struct ConstStringSetHelpers { + size_t operator () (const ConstString& arg) const // for hashing + { + return (size_t)arg.GetCString(); + } + bool operator () (const ConstString& arg1, const ConstString& arg2) const // for equality + { + return arg1.operator==(arg2); + } + }; + typedef std::unordered_set CompleteClassSet; + CompleteClassSet m_negative_complete_class_cache; + + ISAToDescriptorIterator + GetDescriptorIterator (const ConstString &name); + + DISALLOW_COPY_AND_ASSIGN (ObjCLanguageRuntime); +}; + +} // namespace lldb_private + +#endif // liblldb_ObjCLanguageRuntime_h_ diff --git a/include/lldb/Target/OperatingSystem.h b/include/lldb/Target/OperatingSystem.h new file mode 100644 index 00000000000..f1c0eb06026 --- /dev/null +++ b/include/lldb/Target/OperatingSystem.h @@ -0,0 +1,101 @@ +//===-- OperatingSystem.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_OperatingSystem_h_ +#define liblldb_OperatingSystem_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +/// @class OperatingSystem OperatingSystem.h "lldb/Target/OperatingSystem.h" +/// @brief A plug-in interface definition class for halted OS helpers. +/// +/// Halted OS plug-ins can be used by any process to locate and create +/// OS objects, like threads, during the lifetime of a debug session. +/// This is commonly used when attaching to an operating system that is +/// halted, such as when debugging over JTAG or connecting to low level +/// kernel debug services. +//---------------------------------------------------------------------- + +class OperatingSystem : + public PluginInterface + +{ +public: + //------------------------------------------------------------------ + /// Find a halted OS plugin for a given process. + /// + /// Scans the installed OperatingSystem plug-ins and tries to find + /// an instance that matches the current target triple and + /// executable. + /// + /// @param[in] process + /// The process for which to try and locate a halted OS + /// plug-in instance. + /// + /// @param[in] plugin_name + /// An optional name of a specific halted OS plug-in that + /// should be used. If NULL, pick the best plug-in. + //------------------------------------------------------------------ + static OperatingSystem* + FindPlugin (Process *process, const char *plugin_name); + + //------------------------------------------------------------------ + // Class Methods + //------------------------------------------------------------------ + OperatingSystem (Process *process); + + virtual + ~OperatingSystem(); + + //------------------------------------------------------------------ + // Plug-in Methods + //------------------------------------------------------------------ + virtual bool + UpdateThreadList (ThreadList &old_thread_list, + ThreadList &real_thread_list, + ThreadList &new_thread_list) = 0; + + virtual void + ThreadWasSelected (Thread *thread) = 0; + + virtual lldb::RegisterContextSP + CreateRegisterContextForThread (Thread *thread, lldb::addr_t reg_data_addr) = 0; + + virtual lldb::StopInfoSP + CreateThreadStopReason (Thread *thread) = 0; + + virtual lldb::ThreadSP + CreateThread (lldb::tid_t tid, lldb::addr_t context) + { + return lldb::ThreadSP(); + } + + virtual bool + IsOperatingSystemPluginThread (const lldb::ThreadSP &thread_sp); + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Process* m_process; ///< The process that this dynamic loader plug-in is tracking. +private: + DISALLOW_COPY_AND_ASSIGN (OperatingSystem); +}; + +} // namespace lldb_private + +#endif // #ifndef liblldb_OperatingSystem_h_ diff --git a/include/lldb/Target/PathMappingList.h b/include/lldb/Target/PathMappingList.h new file mode 100644 index 00000000000..b5bcbbfd768 --- /dev/null +++ b/include/lldb/Target/PathMappingList.h @@ -0,0 +1,171 @@ +//===-- PathMappingList.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PathMappingList_h_ +#define liblldb_PathMappingList_h_ + +// C Includes +// C++ Includes +#include +#include +// Other libraries and framework includes +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +// Project includes + +namespace lldb_private { + +class PathMappingList +{ +public: + + typedef void (*ChangedCallback) (const PathMappingList &path_list, + void *baton); + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + PathMappingList (); + + PathMappingList (ChangedCallback callback, + void *callback_baton); + + PathMappingList (const PathMappingList &rhs); + + ~PathMappingList (); + + const PathMappingList & + operator =(const PathMappingList &rhs); + + void + Append (const ConstString &path, const ConstString &replacement, bool notify); + + void + Append (const PathMappingList &rhs, bool notify); + + void + Clear (bool notify); + + // By default, dump all pairs. + void + Dump (Stream *s, int pair_index=-1); + + bool + IsEmpty() const + { + return m_pairs.empty(); + } + + size_t + GetSize () const + { + return m_pairs.size(); + } + + bool + GetPathsAtIndex (uint32_t idx, ConstString &path, ConstString &new_path) const; + + void + Insert (const ConstString &path, + const ConstString &replacement, + uint32_t insert_idx, + bool notify); + + bool + Remove (off_t index, bool notify); + + bool + Remove (const ConstString &path, bool notify); + + bool + Replace (const ConstString &path, + const ConstString &replacement, + bool notify); + + bool + Replace (const ConstString &path, + const ConstString &replacement, + uint32_t index, + bool notify); + bool + RemapPath (const ConstString &path, ConstString &new_path) const; + + //------------------------------------------------------------------ + /// Remaps a source file given \a path into \a new_path. + /// + /// Remaps \a path if any source remappings match. This function + /// does NOT stat the file system so it can be used in tight loops + /// where debug info is being parsed. + /// + /// @param[in] path + /// The original source file path to try and remap. + /// + /// @param[out] new_path + /// The newly remapped filespec that is may or may not exist. + /// + /// @return + /// /b true if \a path was successfully located and \a new_path + /// is filled in with a new source path, \b false otherwise. + //------------------------------------------------------------------ + bool + RemapPath (const char *path, std::string &new_path) const; + + + //------------------------------------------------------------------ + /// Finds a source file given a file spec using the path remappings. + /// + /// Tries to resolve \a orig_spec by checking the path remappings. + /// It makes sure the file exists by checking with the file system, + /// so this call can be expensive if the remappings are on a network + /// or are even on the local file system, so use this function + /// sparingly (not in a tight debug info parsing loop). + /// + /// @param[in] orig_spec + /// The original source file path to try and remap. + /// + /// @param[out] new_spec + /// The newly remapped filespec that is guaranteed to exist. + /// + /// @return + /// /b true if \a orig_spec was successfully located and + /// \a new_spec is filled in with an existing file spec, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + FindFile (const FileSpec &orig_spec, FileSpec &new_spec) const; + + uint32_t + FindIndexForPath (const ConstString &path) const; + + uint32_t + GetModificationID() const + { + return m_mod_id; + } +protected: + typedef std::pair pair; + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + iterator + FindIteratorForPath (const ConstString &path); + + const_iterator + FindIteratorForPath (const ConstString &path) const; + + collection m_pairs; + ChangedCallback m_callback; + void * m_callback_baton; + uint32_t m_mod_id; // Incremented anytime anything is added or removed. +}; + +} // namespace lldb_private + +#endif // liblldb_PathMappingList_h_ diff --git a/include/lldb/Target/Platform.h b/include/lldb/Target/Platform.h new file mode 100644 index 00000000000..b0a07946e74 --- /dev/null +++ b/include/lldb/Target/Platform.h @@ -0,0 +1,755 @@ +//===-- Platform.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Platform_h_ +#define liblldb_Platform_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + + //---------------------------------------------------------------------- + /// @class Platform Platform.h "lldb/Target/Platform.h" + /// @brief A plug-in interface definition class for debug platform that + /// includes many platform abilities such as: + /// @li getting platform information such as supported architectures, + /// supported binary file formats and more + /// @li launching new processes + /// @li attaching to existing processes + /// @li download/upload files + /// @li execute shell commands + /// @li listing and getting info for existing processes + /// @li attaching and possibly debugging the platform's kernel + //---------------------------------------------------------------------- + class Platform : public PluginInterface + { + public: + + //------------------------------------------------------------------ + /// 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 in a + /// PlatformSubclass::Initialize() function. + /// + /// This platform will be used as the default platform when launching + /// or attaching to processes unless another platform is specified. + //------------------------------------------------------------------ + static lldb::PlatformSP + GetDefaultPlatform (); + + static lldb::PlatformSP + GetPlatformForArchitecture (const ArchSpec &arch, + ArchSpec *platform_arch_ptr); + + static const char * + GetHostPlatformName (); + + static void + SetDefaultPlatform (const lldb::PlatformSP &platform_sp); + + static lldb::PlatformSP + Create (const char *platform_name, Error &error); + + static lldb::PlatformSP + Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &error); + + static uint32_t + GetNumConnectedRemotePlatforms (); + + static lldb::PlatformSP + GetConnectedRemotePlatformAtIndex (uint32_t idx); + + //------------------------------------------------------------------ + /// Default Constructor + //------------------------------------------------------------------ + Platform (bool is_host_platform); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~Platform(); + + //------------------------------------------------------------------ + /// Find a platform plugin for a given process. + /// + /// Scans the installed Platform plug-ins and tries to find + /// an instance that can be used for \a process + /// + /// @param[in] process + /// The process for which to try and locate a platform + /// plug-in instance. + /// + /// @param[in] plugin_name + /// An optional name of a specific platform plug-in that + /// should be used. If NULL, pick the best plug-in. + //------------------------------------------------------------------ + static Platform* + FindPlugin (Process *process, const ConstString &plugin_name); + + //------------------------------------------------------------------ + /// Set the target's executable based off of the existing + /// architecture information in \a target given a path to an + /// executable \a exe_file. + /// + /// Each platform knows the architectures that it supports and can + /// select the correct architecture slice within \a exe_file by + /// inspecting the architecture in \a target. If the target had an + /// architecture specified, then in can try and obey that request + /// and optionally fail if the architecture doesn't match up. + /// If no architecture is specified, the platform should select the + /// default architecture from \a exe_file. Any application bundles + /// or executable wrappers can also be inspected for the actual + /// application binary within the bundle that should be used. + /// + /// @return + /// Returns \b true if this Platform plug-in was able to find + /// a suitable executable, \b false otherwise. + //------------------------------------------------------------------ + virtual Error + ResolveExecutable (const FileSpec &exe_file, + const ArchSpec &arch, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr); + + + //------------------------------------------------------------------ + /// Find a symbol file given a symbol file module specification. + /// + /// Each platform might have tricks to find symbol files for an + /// executable given information in a symbol file ModuleSpec. Some + /// platforms might also support symbol files that are bundles and + /// know how to extract the right symbol file given a bundle. + /// + /// @param[in] target + /// The target in which we are trying to resolve the symbol file. + /// The target has a list of modules that we might be able to + /// use in order to help find the right symbol file. If the + /// "m_file" or "m_platform_file" entries in the \a sym_spec + /// are filled in, then we might be able to locate a module in + /// the target, extract its UUID and locate a symbol file. + /// If just the "m_uuid" is specified, then we might be able + /// to find the module in the target that matches that UUID + /// and pair the symbol file along with it. If just "m_symbol_file" + /// is specified, we can use a variety of tricks to locate the + /// symbols in an SDK, PDK, or other development kit location. + /// + /// @param[in] sym_spec + /// A module spec that describes some information about the + /// symbol file we are trying to resolve. The ModuleSpec might + /// contain the following: + /// m_file - A full or partial path to an executable from the + /// target (might be empty). + /// m_platform_file - Another executable hint that contains + /// the path to the file as known on the + /// local/remote platform. + /// m_symbol_file - A full or partial path to a symbol file + /// or symbol bundle that should be used when + /// trying to resolve the symbol file. + /// m_arch - The architecture we are looking for when resolving + /// the symbol file. + /// m_uuid - The UUID of the executable and symbol file. This + /// can often be used to match up an exectuable with + /// a symbol file, or resolve an symbol file in a + /// symbol file bundle. + /// + /// @param[out] sym_file + /// The resolved symbol file spec if the returned error + /// indicates succes. + /// + /// @return + /// Returns an error that describes success or failure. + //------------------------------------------------------------------ + virtual Error + ResolveSymbolFile (Target &target, + const ModuleSpec &sym_spec, + FileSpec &sym_file); + + //------------------------------------------------------------------ + /// Resolves the FileSpec to a (possibly) remote path. Remote + /// platforms must override this to resolve to a path on the remote + /// side. + //------------------------------------------------------------------ + virtual bool + ResolveRemotePath (const FileSpec &platform_path, + FileSpec &resolved_platform_path); + + bool + GetOSVersion (uint32_t &major, + uint32_t &minor, + uint32_t &update); + + bool + SetOSVersion (uint32_t major, + uint32_t minor, + uint32_t update); + + bool + GetOSBuildString (std::string &s); + + bool + GetOSKernelDescription (std::string &s); + + // Returns the the hostname if we are connected, else the short plugin + // name. + ConstString + GetName (); + + virtual const char * + GetHostname (); + + virtual const char * + GetDescription () = 0; + + //------------------------------------------------------------------ + /// Report the current status for this platform. + /// + /// The returned string usually involves returning the OS version + /// (if available), and any SDK directory that might be being used + /// for local file caching, and if connected a quick blurb about + /// what this platform is connected to. + //------------------------------------------------------------------ + virtual void + GetStatus (Stream &strm); + + //------------------------------------------------------------------ + // Subclasses must be able to fetch the current OS version + // + // Remote classes must be connected for this to succeed. Local + // subclasses don't need to override this function as it will just + // call the Host::GetOSVersion(). + //------------------------------------------------------------------ + virtual bool + GetRemoteOSVersion () + { + return false; + } + + virtual bool + GetRemoteOSBuildString (std::string &s) + { + s.clear(); + return false; + } + + virtual bool + GetRemoteOSKernelDescription (std::string &s) + { + s.clear(); + return false; + } + + // Remote Platform subclasses need to override this function + virtual ArchSpec + GetRemoteSystemArchitecture () + { + return ArchSpec(); // Return an invalid architecture + } + + virtual const char * + GetUserName (uint32_t uid); + + virtual const char * + GetGroupName (uint32_t gid); + + //------------------------------------------------------------------ + /// Locate a file for a platform. + /// + /// The default implementation of this function will return the same + /// file patch in \a local_file as was in \a platform_file. + /// + /// @param[in] platform_file + /// The platform file path to locate and cache locally. + /// + /// @param[in] uuid_ptr + /// If we know the exact UUID of the file we are looking for, it + /// can be specified. If it is not specified, we might now know + /// the exact file. The UUID is usually some sort of MD5 checksum + /// for the file and is sometimes known by dynamic linkers/loaders. + /// If the UUID is known, it is best to supply it to platform + /// file queries to ensure we are finding the correct file, not + /// just a file at the correct path. + /// + /// @param[out] local_file + /// A locally cached version of the platform file. For platforms + /// that describe the current host computer, this will just be + /// the same file. For remote platforms, this file might come from + /// and SDK directory, or might need to be sync'ed over to the + /// current machine for efficient debugging access. + /// + /// @return + /// An error object. + //------------------------------------------------------------------ + virtual Error + GetFile (const FileSpec &platform_file, + const UUID *uuid_ptr, + FileSpec &local_file); + + //---------------------------------------------------------------------- + // Locate the scripting resource given a module specification. + // + // Locating the file should happen only on the local computer or using + // the current computers global settings. + //---------------------------------------------------------------------- + virtual FileSpecList + LocateExecutableScriptingResources (Target *target, + Module &module); + + virtual Error + GetSharedModule (const ModuleSpec &module_spec, + lldb::ModuleSP &module_sp, + const FileSpecList *module_search_paths_ptr, + lldb::ModuleSP *old_module_sp_ptr, + bool *did_create_ptr); + + virtual Error + ConnectRemote (Args& args); + + virtual Error + DisconnectRemote (); + + //------------------------------------------------------------------ + /// Get the platform's supported architectures in the order in which + /// they should be searched. + /// + /// @param[in] idx + /// A zero based architecture index + /// + /// @param[out] arch + /// A copy of the archgitecture at index if the return value is + /// \b true. + /// + /// @return + /// \b true if \a arch was filled in and is valid, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual bool + GetSupportedArchitectureAtIndex (uint32_t idx, ArchSpec &arch) = 0; + + virtual size_t + GetSoftwareBreakpointTrapOpcode (Target &target, + BreakpointSite *bp_site) = 0; + + //------------------------------------------------------------------ + /// Launch a new process on a platform, not necessarily for + /// debugging, it could be just for running the process. + //------------------------------------------------------------------ + virtual Error + LaunchProcess (ProcessLaunchInfo &launch_info); + + //------------------------------------------------------------------ + /// Lets a platform answer if it is compatible with a given + /// architecture and the target triple contained within. + //------------------------------------------------------------------ + virtual bool + IsCompatibleArchitecture (const ArchSpec &arch, + bool exact_arch_match, + ArchSpec *compatible_arch_ptr); + + //------------------------------------------------------------------ + /// Not all platforms will support debugging a process by spawning + /// somehow halted for a debugger (specified using the + /// "eLaunchFlagDebug" launch flag) and then attaching. If your + /// platform doesn't support this, override this function and return + /// false. + //------------------------------------------------------------------ + virtual bool + CanDebugProcess () + { + return true; + } + + //------------------------------------------------------------------ + /// Subclasses should NOT need to implement this function as it uses + /// the Platform::LaunchProcess() followed by Platform::Attach () + //------------------------------------------------------------------ + lldb::ProcessSP + DebugProcess (ProcessLaunchInfo &launch_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Listener &listener, + Error &error); + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// Each platform subclass needs to implement this function and + /// attempt to attach to the process with the process ID of \a pid. + /// The platform subclass should return an appropriate ProcessSP + /// subclass that is attached to the process, or an empty shared + /// pointer with an appriopriate error. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @return + /// An appropriate ProcessSP containing a valid shared pointer + /// to the default Process subclass for the platform that is + /// attached to the process, or an empty shared pointer with an + /// appriopriate error fill into the \a error object. + //------------------------------------------------------------------ + virtual lldb::ProcessSP + Attach (ProcessAttachInfo &attach_info, + Debugger &debugger, + Target *target, // Can be NULL, if NULL create a new target, else use existing one + Listener &listener, + Error &error) = 0; + + //------------------------------------------------------------------ + /// Attach to an existing process by process name. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call + /// Process::WillAttach (const char *) and if that returns \b + /// true, Process::DoAttach (const char *) will be called to + /// actually do the attach. If DoAttach returns \b true, then + /// Process::DidAttach() will be called. + /// + /// @param[in] process_name + /// A process name to match against the current process list. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ +// virtual lldb::ProcessSP +// Attach (const char *process_name, +// bool wait_for_launch, +// Error &error) = 0; + + //------------------------------------------------------------------ + // The base class Platform will take care of the host platform. + // Subclasses will need to fill in the remote case. + //------------------------------------------------------------------ + virtual uint32_t + FindProcesses (const ProcessInstanceInfoMatch &match_info, + ProcessInstanceInfoList &proc_infos); + + virtual bool + GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); + + //------------------------------------------------------------------ + // Set a breakpoint on all functions that can end up creating a thread + // for this platform. This is needed when running expressions and + // also for process control. + //------------------------------------------------------------------ + virtual lldb::BreakpointSP + SetThreadCreationBreakpoint (Target &target); + + + const std::string & + GetRemoteURL () const + { + return m_remote_url; + } + + bool + IsHost () const + { + return m_is_host; // Is this the default host platform? + } + + bool + IsRemote () const + { + return !m_is_host; + } + + virtual bool + IsConnected () const + { + // Remote subclasses should override this function + return IsHost(); + } + + const ArchSpec & + GetSystemArchitecture(); + + void + SetSystemArchitecture (const ArchSpec &arch) + { + m_system_arch = arch; + if (IsHost()) + m_os_version_set_while_connected = m_system_arch.IsValid(); + } + + // Used for column widths + size_t + GetMaxUserIDNameLength() const + { + return m_max_uid_name_len; + } + // Used for column widths + size_t + GetMaxGroupIDNameLength() const + { + return m_max_gid_name_len; + } + + const ConstString & + GetSDKRootDirectory () const + { + return m_sdk_sysroot; + } + + void + SetSDKRootDirectory (const ConstString &dir) + { + m_sdk_sysroot = dir; + } + + const ConstString & + GetSDKBuild () const + { + return m_sdk_build; + } + + void + SetSDKBuild (const ConstString &sdk_build) + { + m_sdk_build = sdk_build; + } + + // There may be modules that we don't want to find by default for operations like "setting breakpoint by name". + // The platform will return "true" from this call if the passed in module happens to be one of these. + + virtual bool + ModuleIsExcludedForNonModuleSpecificSearches (Target &target, const lldb::ModuleSP &module_sp) + { + return false; + } + + virtual size_t + GetEnvironment (StringList &environment); + + protected: + bool m_is_host; + // Set to true when we are able to actually set the OS version while + // being connected. For remote platforms, we might set the version ahead + // of time before we actually connect and this version might change when + // we actually connect to a remote platform. For the host platform this + // will be set to the once we call Host::GetOSVersion(). + bool m_os_version_set_while_connected; + bool m_system_arch_set_while_connected; + ConstString m_sdk_sysroot; // the root location of where the SDK files are all located + ConstString m_sdk_build; + std::string m_remote_url; + std::string m_name; + uint32_t m_major_os_version; + uint32_t m_minor_os_version; + uint32_t m_update_os_version; + ArchSpec m_system_arch; // The architecture of the kernel or the remote platform + typedef std::map IDToNameMap; + Mutex m_uid_map_mutex; + Mutex m_gid_map_mutex; + IDToNameMap m_uid_map; + IDToNameMap m_gid_map; + size_t m_max_uid_name_len; + size_t m_max_gid_name_len; + + const char * + GetCachedUserName (uint32_t uid) + { + Mutex::Locker locker (m_uid_map_mutex); + IDToNameMap::iterator pos = m_uid_map.find (uid); + if (pos != m_uid_map.end()) + { + // return the empty string if our string is NULL + // so we can tell when things were in the negative + // cached (didn't find a valid user name, don't keep + // trying) + return pos->second.AsCString(""); + } + return NULL; + } + + const char * + SetCachedUserName (uint32_t uid, const char *name, size_t name_len) + { + Mutex::Locker locker (m_uid_map_mutex); + ConstString const_name (name); + m_uid_map[uid] = const_name; + if (m_max_uid_name_len < name_len) + m_max_uid_name_len = name_len; + // Const strings lives forever in our const string pool, so we can return the const char * + return const_name.GetCString(); + } + + void + SetUserNameNotFound (uint32_t uid) + { + Mutex::Locker locker (m_uid_map_mutex); + m_uid_map[uid] = ConstString(); + } + + + void + ClearCachedUserNames () + { + Mutex::Locker locker (m_uid_map_mutex); + m_uid_map.clear(); + } + + const char * + GetCachedGroupName (uint32_t gid) + { + Mutex::Locker locker (m_gid_map_mutex); + IDToNameMap::iterator pos = m_gid_map.find (gid); + if (pos != m_gid_map.end()) + { + // return the empty string if our string is NULL + // so we can tell when things were in the negative + // cached (didn't find a valid group name, don't keep + // trying) + return pos->second.AsCString(""); + } + return NULL; + } + + const char * + SetCachedGroupName (uint32_t gid, const char *name, size_t name_len) + { + Mutex::Locker locker (m_gid_map_mutex); + ConstString const_name (name); + m_gid_map[gid] = const_name; + if (m_max_gid_name_len < name_len) + m_max_gid_name_len = name_len; + // Const strings lives forever in our const string pool, so we can return the const char * + return const_name.GetCString(); + } + + void + SetGroupNameNotFound (uint32_t gid) + { + Mutex::Locker locker (m_gid_map_mutex); + m_gid_map[gid] = ConstString(); + } + + void + ClearCachedGroupNames () + { + Mutex::Locker locker (m_gid_map_mutex); + m_gid_map.clear(); + } + + private: + DISALLOW_COPY_AND_ASSIGN (Platform); + }; + + + class PlatformList + { + public: + PlatformList() : + m_mutex (Mutex::eMutexTypeRecursive), + m_platforms (), + m_selected_platform_sp() + { + } + + ~PlatformList() + { + } + + void + Append (const lldb::PlatformSP &platform_sp, bool set_selected) + { + Mutex::Locker locker (m_mutex); + m_platforms.push_back (platform_sp); + if (set_selected) + m_selected_platform_sp = m_platforms.back(); + } + + size_t + GetSize() + { + Mutex::Locker locker (m_mutex); + return m_platforms.size(); + } + + lldb::PlatformSP + GetAtIndex (uint32_t idx) + { + lldb::PlatformSP platform_sp; + { + Mutex::Locker locker (m_mutex); + if (idx < m_platforms.size()) + platform_sp = m_platforms[idx]; + } + return platform_sp; + } + + //------------------------------------------------------------------ + /// Select the active platform. + /// + /// In order to debug remotely, other platform's can be remotely + /// connected to and set as the selected platform for any subsequent + /// debugging. This allows connection to remote targets and allows + /// the ability to discover process info, launch and attach to remote + /// processes. + //------------------------------------------------------------------ + lldb::PlatformSP + GetSelectedPlatform () + { + Mutex::Locker locker (m_mutex); + if (!m_selected_platform_sp && !m_platforms.empty()) + m_selected_platform_sp = m_platforms.front(); + + return m_selected_platform_sp; + } + + void + SetSelectedPlatform (const lldb::PlatformSP &platform_sp) + { + if (platform_sp) + { + Mutex::Locker locker (m_mutex); + const size_t num_platforms = m_platforms.size(); + for (size_t idx=0; idx collection; + mutable Mutex m_mutex; + collection m_platforms; + lldb::PlatformSP m_selected_platform_sp; + + private: + DISALLOW_COPY_AND_ASSIGN (PlatformList); + }; +} // namespace lldb_private + +#endif // liblldb_Platform_h_ diff --git a/include/lldb/Target/Process.h b/include/lldb/Target/Process.h new file mode 100644 index 00000000000..ef89a1eb141 --- /dev/null +++ b/include/lldb/Target/Process.h @@ -0,0 +1,3786 @@ +//===-- Process.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Process_h_ +#define liblldb_Process_h_ + +// C Includes +#include +#include + +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/RangeMap.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/ThreadSafeValue.h" +#include "lldb/Core/PluginInterface.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Breakpoint/BreakpointSiteList.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/IRDynamicChecks.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/ProcessRunLock.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Memory.h" +#include "lldb/Target/ThreadList.h" +#include "lldb/Target/UnixSignals.h" +#include "lldb/Utility/PseudoTerminal.h" + +namespace lldb_private { + +//---------------------------------------------------------------------- +// ProcessProperties +//---------------------------------------------------------------------- +class ProcessProperties : public Properties +{ +public: + ProcessProperties(bool is_global); + + virtual + ~ProcessProperties(); + + bool + GetDisableMemoryCache() const; + + Args + GetExtraStartupCommands () const; + + void + SetExtraStartupCommands (const Args &args); + + FileSpec + GetPythonOSPluginPath () const; + + void + SetPythonOSPluginPath (const FileSpec &file); + + bool + GetIgnoreBreakpointsInExpressions () const; + + void + SetIgnoreBreakpointsInExpressions (bool ignore); + + bool + GetUnwindOnErrorInExpressions () const; + + void + SetUnwindOnErrorInExpressions (bool ignore); + + bool + GetStopOnSharedLibraryEvents () const; + + void + SetStopOnSharedLibraryEvents (bool stop); + + bool + GetDetachKeepsStopped () const; + + void + SetDetachKeepsStopped (bool keep_stopped); +}; + +typedef std::shared_ptr ProcessPropertiesSP; + +//---------------------------------------------------------------------- +// ProcessInfo +// +// A base class for information for a process. This can be used to fill +// out information for a process prior to launching it, or it can be +// used for an instance of a process and can be filled in with the +// existing values for that process. +//---------------------------------------------------------------------- +class ProcessInfo +{ +public: + ProcessInfo () : + m_executable (), + m_arguments (), + m_environment (), + m_uid (UINT32_MAX), + m_gid (UINT32_MAX), + m_arch(), + m_pid (LLDB_INVALID_PROCESS_ID) + { + } + + ProcessInfo (const char *name, + const ArchSpec &arch, + lldb::pid_t pid) : + m_executable (name, false), + m_arguments (), + m_environment(), + m_uid (UINT32_MAX), + m_gid (UINT32_MAX), + m_arch (arch), + m_pid (pid) + { + } + + void + 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 * + GetName() const + { + return m_executable.GetFilename().GetCString(); + } + + size_t + GetNameLength() const + { + return m_executable.GetFilename().GetLength(); + } + + FileSpec & + GetExecutableFile () + { + return m_executable; + } + + void + 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) + { + char filename[PATH_MAX]; + if (exe_file.GetPath(filename, sizeof(filename))) + m_arguments.InsertArgumentAtIndex (0, filename); + } + } + else + { + m_executable.Clear(); + } + } + + const FileSpec & + GetExecutableFile () const + { + return m_executable; + } + + uint32_t + GetUserID() const + { + return m_uid; + } + + uint32_t + GetGroupID() const + { + return m_gid; + } + + bool + UserIDIsValid () const + { + return m_uid != UINT32_MAX; + } + + bool + GroupIDIsValid () const + { + return m_gid != UINT32_MAX; + } + + void + SetUserID (uint32_t uid) + { + m_uid = uid; + } + + void + SetGroupID (uint32_t gid) + { + m_gid = gid; + } + + ArchSpec & + GetArchitecture () + { + return m_arch; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + lldb::pid_t + GetProcessID () const + { + return m_pid; + } + + void + SetProcessID (lldb::pid_t pid) + { + m_pid = pid; + } + + bool + ProcessIDIsValid() const + { + return m_pid != LLDB_INVALID_PROCESS_ID; + } + + void + Dump (Stream &s, Platform *platform) const; + + Args & + GetArguments () + { + return m_arguments; + } + + const Args & + GetArguments () const + { + return m_arguments; + } + + const char * + GetArg0 () const + { + if (m_arg0.empty()) + return NULL; + return m_arg0.c_str(); + } + + void + SetArg0 (const char *arg) + { + if (arg && arg[0]) + m_arg0 = arg; + else + m_arg0.clear(); + } + + void + SetArguments (const Args& args, bool first_arg_is_executable); + + void + SetArguments (char const **argv, bool first_arg_is_executable); + + Args & + GetEnvironmentEntries () + { + return m_environment; + } + + const Args & + GetEnvironmentEntries () const + { + return m_environment; + } + +protected: + FileSpec m_executable; + std::string m_arg0; // argv[0] if supported. If empty, then use m_executable. + // Not all process plug-ins support specifying an argv[0] + // that differs from the resolved platform executable + // (which is in m_executable) + Args m_arguments; // All program arguments except argv[0] + Args m_environment; + uint32_t m_uid; + uint32_t m_gid; + ArchSpec m_arch; + lldb::pid_t m_pid; +}; + +//---------------------------------------------------------------------- +// ProcessInstanceInfo +// +// Describes an existing process and any discoverable information that +// pertains to that process. +//---------------------------------------------------------------------- +class ProcessInstanceInfo : public ProcessInfo +{ +public: + ProcessInstanceInfo () : + ProcessInfo (), + m_euid (UINT32_MAX), + m_egid (UINT32_MAX), + m_parent_pid (LLDB_INVALID_PROCESS_ID) + { + } + + ProcessInstanceInfo (const char *name, + const ArchSpec &arch, + lldb::pid_t pid) : + ProcessInfo (name, arch, pid), + m_euid (UINT32_MAX), + m_egid (UINT32_MAX), + m_parent_pid (LLDB_INVALID_PROCESS_ID) + { + } + + void + Clear () + { + ProcessInfo::Clear(); + m_euid = UINT32_MAX; + m_egid = UINT32_MAX; + m_parent_pid = LLDB_INVALID_PROCESS_ID; + } + + uint32_t + GetEffectiveUserID() const + { + return m_euid; + } + + uint32_t + GetEffectiveGroupID() const + { + return m_egid; + } + + bool + EffectiveUserIDIsValid () const + { + return m_euid != UINT32_MAX; + } + + bool + EffectiveGroupIDIsValid () const + { + return m_egid != UINT32_MAX; + } + + void + SetEffectiveUserID (uint32_t uid) + { + m_euid = uid; + } + + void + SetEffectiveGroupID (uint32_t gid) + { + m_egid = gid; + } + + lldb::pid_t + GetParentProcessID () const + { + return m_parent_pid; + } + + void + SetParentProcessID (lldb::pid_t pid) + { + m_parent_pid = pid; + } + + bool + ParentProcessIDIsValid() const + { + return m_parent_pid != LLDB_INVALID_PROCESS_ID; + } + + void + Dump (Stream &s, Platform *platform) const; + + static void + DumpTableHeader (Stream &s, Platform *platform, bool show_args, bool verbose); + + void + DumpAsTableRow (Stream &s, Platform *platform, bool show_args, bool verbose) const; + +protected: + uint32_t m_euid; + uint32_t m_egid; + lldb::pid_t m_parent_pid; +}; + + +//---------------------------------------------------------------------- +// ProcessLaunchInfo +// +// Describes any information that is required to launch a process. +//---------------------------------------------------------------------- + +class ProcessLaunchInfo : public ProcessInfo +{ +public: + + class FileAction + { + public: + enum Action + { + eFileActionNone, + eFileActionClose, + eFileActionDuplicate, + eFileActionOpen + }; + + + FileAction () : + m_action (eFileActionNone), + m_fd (-1), + m_arg (-1), + m_path () + { + } + + void + Clear() + { + m_action = eFileActionNone; + m_fd = -1; + m_arg = -1; + m_path.clear(); + } + + bool + Close (int fd); + + bool + Duplicate (int fd, int dup_fd); + + bool + Open (int fd, const char *path, bool read, bool write); + + static bool + AddPosixSpawnFileAction (posix_spawn_file_actions_t *file_actions, + const FileAction *info, + Log *log, + Error& error); + + int + GetFD () const + { + return m_fd; + } + + Action + GetAction () const + { + return m_action; + } + + int + GetActionArgument () const + { + return m_arg; + } + + const char * + GetPath () const + { + if (m_path.empty()) + return NULL; + return m_path.c_str(); + } + + protected: + Action m_action; // The action for this file + int m_fd; // An existing file descriptor + int m_arg; // oflag for eFileActionOpen*, dup_fd for eFileActionDuplicate + std::string m_path; // A file path to use for opening after fork or posix_spawn + }; + + ProcessLaunchInfo () : + ProcessInfo(), + m_working_dir (), + m_plugin_name (), + m_shell (), + m_flags (0), + m_file_actions (), + m_pty (), + m_resume_count (0), + m_monitor_callback (NULL), + m_monitor_callback_baton (NULL), + m_monitor_signals (false) + { + } + + ProcessLaunchInfo (const char *stdin_path, + const char *stdout_path, + const char *stderr_path, + const char *working_directory, + uint32_t launch_flags) : + ProcessInfo(), + m_working_dir (), + m_plugin_name (), + m_shell (), + m_flags (launch_flags), + m_file_actions (), + m_pty (), + m_resume_count (0), + m_monitor_callback (NULL), + m_monitor_callback_baton (NULL), + m_monitor_signals (false) + { + if (stdin_path) + { + ProcessLaunchInfo::FileAction file_action; + const bool read = true; + const bool write = false; + if (file_action.Open(STDIN_FILENO, stdin_path, read, write)) + AppendFileAction (file_action); + } + if (stdout_path) + { + ProcessLaunchInfo::FileAction file_action; + const bool read = false; + const bool write = true; + if (file_action.Open(STDOUT_FILENO, stdout_path, read, write)) + AppendFileAction (file_action); + } + if (stderr_path) + { + ProcessLaunchInfo::FileAction file_action; + const bool read = false; + const bool write = true; + if (file_action.Open(STDERR_FILENO, stderr_path, read, write)) + AppendFileAction (file_action); + } + if (working_directory) + SetWorkingDirectory(working_directory); + } + + void + AppendFileAction (const FileAction &info) + { + m_file_actions.push_back(info); + } + + bool + AppendCloseFileAction (int fd) + { + FileAction file_action; + if (file_action.Close (fd)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + bool + AppendDuplicateFileAction (int fd, int dup_fd) + { + FileAction file_action; + if (file_action.Duplicate (fd, dup_fd)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + bool + AppendOpenFileAction (int fd, const char *path, bool read, bool write) + { + FileAction file_action; + if (file_action.Open (fd, path, read, write)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + bool + AppendSuppressFileAction (int fd, bool read, bool write) + { + FileAction file_action; + if (file_action.Open (fd, "/dev/null", read, write)) + { + AppendFileAction (file_action); + return true; + } + return false; + } + + void + FinalizeFileActions (Target *target, + bool default_to_use_pty); + + size_t + GetNumFileActions () const + { + return m_file_actions.size(); + } + + const FileAction * + GetFileActionAtIndex (size_t idx) const + { + if (idx < m_file_actions.size()) + return &m_file_actions[idx]; + return NULL; + } + + const FileAction * + 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 NULL; + } + + Flags & + GetFlags () + { + return m_flags; + } + + const Flags & + GetFlags () const + { + return m_flags; + } + + const char * + GetWorkingDirectory () const + { + if (m_working_dir.empty()) + return NULL; + return m_working_dir.c_str(); + } + + void + SetWorkingDirectory (const char *working_dir) + { + if (working_dir && working_dir[0]) + m_working_dir.assign (working_dir); + else + m_working_dir.clear(); + } + + void + SwapWorkingDirectory (std::string &working_dir) + { + m_working_dir.swap (working_dir); + } + + + const char * + GetProcessPluginName () const + { + if (m_plugin_name.empty()) + return NULL; + return m_plugin_name.c_str(); + } + + void + SetProcessPluginName (const char *plugin) + { + if (plugin && plugin[0]) + m_plugin_name.assign (plugin); + else + m_plugin_name.clear(); + } + + const char * + GetShell () const + { + if (m_shell.empty()) + return NULL; + return m_shell.c_str(); + } + + void + SetShell (const char * path) + { + if (path && path[0]) + { + m_shell.assign (path); + m_flags.Set (lldb::eLaunchFlagLaunchInShell); + } + else + { + m_shell.clear(); + m_flags.Clear (lldb::eLaunchFlagLaunchInShell); + } + } + + uint32_t + GetResumeCount () const + { + return m_resume_count; + } + + void + SetResumeCount (uint32_t c) + { + m_resume_count = c; + } + + bool + GetLaunchInSeparateProcessGroup () + { + return m_flags.Test(lldb::eLaunchFlagLaunchInSeparateProcessGroup); + } + + void + SetLaunchInSeparateProcessGroup (bool separate) + { + if (separate) + m_flags.Set(lldb::eLaunchFlagLaunchInSeparateProcessGroup); + else + m_flags.Clear (lldb::eLaunchFlagLaunchInSeparateProcessGroup); + + } + + void + 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; + } + + bool + ConvertArgumentsForLaunchingInShell (Error &error, + bool localhost, + bool will_debug, + bool first_arg_is_full_shell_command); + + void + SetMonitorProcessCallback (Host::MonitorChildProcessCallback callback, + void *baton, + bool monitor_signals) + { + m_monitor_callback = callback; + m_monitor_callback_baton = baton; + m_monitor_signals = monitor_signals; + } + + bool + MonitorProcess () const + { + if (m_monitor_callback && ProcessIDIsValid()) + { + Host::StartMonitoringChildProcess (m_monitor_callback, + m_monitor_callback_baton, + GetProcessID(), + m_monitor_signals); + return true; + } + return false; + } + + lldb_utility::PseudoTerminal & + GetPTY () + { + return m_pty; + } + +protected: + std::string m_working_dir; + std::string m_plugin_name; + std::string m_shell; + Flags m_flags; // Bitwise OR of bits from lldb::LaunchFlags + std::vector m_file_actions; // File actions for any other files + lldb_utility::PseudoTerminal m_pty; + uint32_t m_resume_count; // How many times do we resume after launching + Host::MonitorChildProcessCallback m_monitor_callback; + void *m_monitor_callback_baton; + bool m_monitor_signals; + +}; + +//---------------------------------------------------------------------- +// ProcessLaunchInfo +// +// Describes any information that is required to launch a process. +//---------------------------------------------------------------------- + +class ProcessAttachInfo : public ProcessInstanceInfo +{ +public: + ProcessAttachInfo() : + ProcessInstanceInfo(), + m_plugin_name (), + m_resume_count (0), + m_wait_for_launch (false), + m_ignore_existing (true), + m_continue_once_attached (false) + { + } + + ProcessAttachInfo (const ProcessLaunchInfo &launch_info) : + ProcessInstanceInfo(), + m_plugin_name (), + m_resume_count (0), + m_wait_for_launch (false), + m_ignore_existing (true), + m_continue_once_attached (false) + { + ProcessInfo::operator= (launch_info); + SetProcessPluginName (launch_info.GetProcessPluginName()); + SetResumeCount (launch_info.GetResumeCount()); + } + + bool + GetWaitForLaunch () const + { + return m_wait_for_launch; + } + + void + SetWaitForLaunch (bool b) + { + m_wait_for_launch = b; + } + + bool + GetIgnoreExisting () const + { + return m_ignore_existing; + } + + void + SetIgnoreExisting (bool b) + { + m_ignore_existing = b; + } + + bool + GetContinueOnceAttached () const + { + return m_continue_once_attached; + } + + void + SetContinueOnceAttached (bool b) + { + m_continue_once_attached = b; + } + + uint32_t + GetResumeCount () const + { + return m_resume_count; + } + + void + SetResumeCount (uint32_t c) + { + m_resume_count = c; + } + + const char * + GetProcessPluginName () const + { + if (m_plugin_name.empty()) + return NULL; + return m_plugin_name.c_str(); + } + + void + SetProcessPluginName (const char *plugin) + { + if (plugin && plugin[0]) + m_plugin_name.assign (plugin); + else + m_plugin_name.clear(); + } + + void + Clear () + { + ProcessInstanceInfo::Clear(); + m_plugin_name.clear(); + m_resume_count = 0; + m_wait_for_launch = false; + m_ignore_existing = true; + m_continue_once_attached = false; + } + + bool + ProcessInfoSpecified () const + { + if (GetExecutableFile()) + return true; + if (GetProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + if (GetParentProcessID() != LLDB_INVALID_PROCESS_ID) + return true; + return false; + } +protected: + std::string m_plugin_name; + uint32_t m_resume_count; // How many times do we resume after launching + bool m_wait_for_launch; + bool m_ignore_existing; + bool m_continue_once_attached; // Supports the use-case scenario of immediately continuing the process once attached. +}; + +class ProcessLaunchCommandOptions : public Options +{ +public: + + ProcessLaunchCommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~ProcessLaunchCommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting () + { + launch_info.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + ProcessLaunchInfo launch_info; +}; + +//---------------------------------------------------------------------- +// ProcessInstanceInfoMatch +// +// A class to help matching one ProcessInstanceInfo to another. +//---------------------------------------------------------------------- + +class ProcessInstanceInfoMatch +{ +public: + ProcessInstanceInfoMatch () : + m_match_info (), + m_name_match_type (eNameMatchIgnore), + m_match_all_users (false) + { + } + + ProcessInstanceInfoMatch (const char *process_name, + NameMatchType process_name_match_type) : + m_match_info (), + m_name_match_type (process_name_match_type), + m_match_all_users (false) + { + m_match_info.GetExecutableFile().SetFile(process_name, false); + } + + ProcessInstanceInfo & + GetProcessInfo () + { + return m_match_info; + } + + const ProcessInstanceInfo & + GetProcessInfo () const + { + return m_match_info; + } + + bool + GetMatchAllUsers () const + { + return m_match_all_users; + } + + void + SetMatchAllUsers (bool b) + { + m_match_all_users = b; + } + + NameMatchType + GetNameMatchType () const + { + return m_name_match_type; + } + + void + SetNameMatchType (NameMatchType name_match_type) + { + m_name_match_type = name_match_type; + } + + bool + NameMatches (const char *process_name) const; + + bool + Matches (const ProcessInstanceInfo &proc_info) const; + + bool + MatchAllProcesses () const; + void + Clear (); + +protected: + ProcessInstanceInfo m_match_info; + NameMatchType m_name_match_type; + bool m_match_all_users; +}; + +class ProcessInstanceInfoList +{ +public: + ProcessInstanceInfoList () : + m_infos() + { + } + + void + Clear() + { + m_infos.clear(); + } + + size_t + GetSize() + { + return m_infos.size(); + } + + void + Append (const ProcessInstanceInfo &info) + { + m_infos.push_back (info); + } + + const char * + GetProcessNameAtIndex (size_t idx) + { + if (idx < m_infos.size()) + return m_infos[idx].GetName(); + return NULL; + } + + size_t + GetProcessNameLengthAtIndex (size_t idx) + { + if (idx < m_infos.size()) + return m_infos[idx].GetNameLength(); + return 0; + } + + lldb::pid_t + GetProcessIDAtIndex (size_t idx) + { + if (idx < m_infos.size()) + return m_infos[idx].GetProcessID(); + return 0; + } + + bool + GetInfoAtIndex (size_t idx, ProcessInstanceInfo &info) + { + if (idx < m_infos.size()) + { + info = m_infos[idx]; + return true; + } + return false; + } + + // You must ensure "idx" is valid before calling this function + const ProcessInstanceInfo & + GetProcessInfoAtIndex (size_t idx) const + { + assert (idx < m_infos.size()); + return m_infos[idx]; + } + +protected: + typedef std::vector collection; + collection m_infos; +}; + + +// This class tracks the Modification state of the process. Things that can currently modify +// the program are running the program (which will up the StopID) and writing memory (which +// will up the MemoryID.) +// FIXME: Should we also include modification of register states? + +class ProcessModID +{ +friend bool operator== (const ProcessModID &lhs, const ProcessModID &rhs); +public: + ProcessModID () : + m_stop_id (0), + m_last_natural_stop_id(0), + m_resume_id (0), + m_memory_id (0), + m_last_user_expression_resume (0), + m_running_user_expression (false) + {} + + ProcessModID (const ProcessModID &rhs) : + m_stop_id (rhs.m_stop_id), + m_memory_id (rhs.m_memory_id) + {} + + const ProcessModID & operator= (const ProcessModID &rhs) + { + if (this != &rhs) + { + m_stop_id = rhs.m_stop_id; + m_memory_id = rhs.m_memory_id; + } + return *this; + } + + ~ProcessModID () {} + + void BumpStopID () { + m_stop_id++; + if (!IsLastResumeForUserExpression()) + m_last_natural_stop_id++; + } + + void BumpMemoryID () { m_memory_id++; } + + void BumpResumeID () { + m_resume_id++; + if (m_running_user_expression > 0) + m_last_user_expression_resume = m_resume_id; + } + + uint32_t GetStopID() const { return m_stop_id; } + uint32_t GetLastNaturalStopID() const { return m_last_natural_stop_id; } + uint32_t GetMemoryID () const { return m_memory_id; } + uint32_t GetResumeID () const { return m_resume_id; } + uint32_t GetLastUserExpressionResumeID () const { return m_last_user_expression_resume; } + + bool MemoryIDEqual (const ProcessModID &compare) const + { + return m_memory_id == compare.m_memory_id; + } + + bool StopIDEqual (const ProcessModID &compare) const + { + return m_stop_id == compare.m_stop_id; + } + + void SetInvalid () + { + m_stop_id = UINT32_MAX; + } + + bool IsValid () const + { + return m_stop_id != UINT32_MAX; + } + + bool + IsLastResumeForUserExpression () const + { + return m_resume_id == m_last_user_expression_resume; + } + + void + SetRunningUserExpression (bool on) + { + // REMOVEME printf ("Setting running user expression %s at resume id %d - value: %d.\n", on ? "on" : "off", m_resume_id, m_running_user_expression); + if (on) + m_running_user_expression++; + else + m_running_user_expression--; + } + +private: + uint32_t m_stop_id; + uint32_t m_last_natural_stop_id; + uint32_t m_resume_id; + uint32_t m_memory_id; + uint32_t m_last_user_expression_resume; + uint32_t m_running_user_expression; +}; +inline bool operator== (const ProcessModID &lhs, const ProcessModID &rhs) +{ + if (lhs.StopIDEqual (rhs) + && lhs.MemoryIDEqual (rhs)) + return true; + else + return false; +} + +inline bool operator!= (const ProcessModID &lhs, const ProcessModID &rhs) +{ + if (!lhs.StopIDEqual (rhs) + || !lhs.MemoryIDEqual (rhs)) + return true; + else + return false; +} + +class MemoryRegionInfo +{ +public: + typedef Range RangeType; + + enum OptionalBool { + eDontKnow = -1, + eNo = 0, + eYes = 1 + }; + + MemoryRegionInfo () : + m_range (), + m_read (eDontKnow), + m_write (eDontKnow), + m_execute (eDontKnow) + { + } + + ~MemoryRegionInfo () + { + } + + RangeType & + GetRange() + { + return m_range; + } + + void + Clear() + { + m_range.Clear(); + m_read = m_write = m_execute = eDontKnow; + } + + const RangeType & + GetRange() const + { + return m_range; + } + + OptionalBool + GetReadable () const + { + return m_read; + } + + OptionalBool + GetWritable () const + { + return m_write; + } + + OptionalBool + GetExecutable () const + { + return m_execute; + } + + void + SetReadable (OptionalBool val) + { + m_read = val; + } + + void + SetWritable (OptionalBool val) + { + m_write = val; + } + + void + SetExecutable (OptionalBool val) + { + m_execute = val; + } + +protected: + RangeType m_range; + OptionalBool m_read; + OptionalBool m_write; + OptionalBool m_execute; +}; + +//---------------------------------------------------------------------- +/// @class Process Process.h "lldb/Target/Process.h" +/// @brief A plug-in interface definition class for debugging a process. +//---------------------------------------------------------------------- +class Process : + public std::enable_shared_from_this, + public ProcessProperties, + public UserID, + public Broadcaster, + public ExecutionContextScope, + public PluginInterface +{ +friend class ThreadList; +friend class ClangFunction; // For WaitForStateChangeEventsPrivate +friend class CommandObjectProcessLaunch; +friend class ProcessEventData; +friend class CommandObjectBreakpointCommand; +friend class StopInfo; + +public: + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStateChanged = (1 << 0), + eBroadcastBitInterrupt = (1 << 1), + eBroadcastBitSTDOUT = (1 << 2), + eBroadcastBitSTDERR = (1 << 3), + eBroadcastBitProfileData = (1 << 4) + }; + + enum + { + eBroadcastInternalStateControlStop = (1<<0), + eBroadcastInternalStateControlPause = (1<<1), + eBroadcastInternalStateControlResume = (1<<2) + }; + + typedef Range LoadRange; + // We use a read/write lock to allow on or more clients to + // access the process state while the process is stopped (reader). + // We lock the write lock to control access to the process + // while it is running (readers, or clients that want the process + // stopped can block waiting for the process to stop, or just + // try to lock it to see if they can immediately access the stopped + // process. If the try read lock fails, then the process is running. + typedef ProcessRunLock::ProcessRunLocker StopLocker; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + + //------------------------------------------------------------------ + /// A notification structure that can be used by clients to listen + /// for changes in a process's lifetime. + /// + /// @see RegisterNotificationCallbacks (const Notifications&) + /// @see UnregisterNotificationCallbacks (const Notifications&) + //------------------------------------------------------------------ +#ifndef SWIG + typedef struct + { + void *baton; + void (*initialize)(void *baton, Process *process); + void (*process_state_changed) (void *baton, Process *process, lldb::StateType state); + } Notifications; + + class ProcessEventData : + public EventData + { + friend class Process; + + public: + ProcessEventData (); + ProcessEventData (const lldb::ProcessSP &process, lldb::StateType state); + + virtual ~ProcessEventData(); + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + const lldb::ProcessSP & + GetProcessSP() const + { + return m_process_sp; + } + lldb::StateType + GetState() const + { + return m_state; + } + bool + GetRestarted () const + { + return m_restarted; + } + + size_t + GetNumRestartedReasons () + { + return m_restarted_reasons.size(); + } + + const char * + GetRestartedReasonAtIndex(size_t idx) + { + if (idx > m_restarted_reasons.size()) + return NULL; + else + return m_restarted_reasons[idx].c_str(); + } + + bool + GetInterrupted () const + { + return m_interrupted; + } + + virtual void + Dump (Stream *s) const; + + virtual void + DoOnRemoval (Event *event_ptr); + + static const Process::ProcessEventData * + GetEventDataFromEvent (const Event *event_ptr); + + static lldb::ProcessSP + GetProcessFromEvent (const Event *event_ptr); + + static lldb::StateType + GetStateFromEvent (const Event *event_ptr); + + static bool + GetRestartedFromEvent (const Event *event_ptr); + + static size_t + GetNumRestartedReasons(const Event *event_ptr); + + static const char * + GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx); + + static void + AddRestartedReason (Event *event_ptr, const char *reason); + + static void + SetRestartedInEvent (Event *event_ptr, bool new_value); + + static bool + GetInterruptedFromEvent (const Event *event_ptr); + + static void + SetInterruptedInEvent (Event *event_ptr, bool new_value); + + static bool + SetUpdateStateOnRemoval (Event *event_ptr); + + private: + + void + SetUpdateStateOnRemoval() + { + m_update_state++; + } + void + SetRestarted (bool new_value) + { + m_restarted = new_value; + } + void + SetInterrupted (bool new_value) + { + m_interrupted = new_value; + } + void + AddRestartedReason (const char *reason) + { + m_restarted_reasons.push_back(reason); + } + + lldb::ProcessSP m_process_sp; + lldb::StateType m_state; + std::vector m_restarted_reasons; + bool m_restarted; // For "eStateStopped" events, this is true if the target was automatically restarted. + int m_update_state; + bool m_interrupted; + DISALLOW_COPY_AND_ASSIGN (ProcessEventData); + + }; + +#endif + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + + static const ProcessPropertiesSP & + GetGlobalProperties(); + + //------------------------------------------------------------------ + /// Construct with a shared pointer to a target, and the Process listener. + //------------------------------------------------------------------ + Process(Target &target, Listener &listener); + + //------------------------------------------------------------------ + /// Destructor. + /// + /// The destructor is virtual since this class is designed to be + /// inherited from by the plug-in instance. + //------------------------------------------------------------------ + virtual + ~Process(); + + //------------------------------------------------------------------ + /// Find a Process plug-in that can debug \a module using the + /// currently selected architecture. + /// + /// Scans all loaded plug-in interfaces that implement versions of + /// the Process plug-in interface and returns the first instance + /// that can debug the file. + /// + /// @param[in] module_sp + /// The module shared pointer that this process will debug. + /// + /// @param[in] plugin_name + /// If NULL, select the best plug-in for the binary. If non-NULL + /// then look for a plugin whose PluginInfo's name matches + /// this string. + /// + /// @see Process::CanDebug () + //------------------------------------------------------------------ + static lldb::ProcessSP + FindPlugin (Target &target, + const char *plugin_name, + Listener &listener, + const FileSpec *crash_file_path); + + + + //------------------------------------------------------------------ + /// Static function that can be used with the \b host function + /// Host::StartMonitoringChildProcess (). + /// + /// This function can be used by lldb_private::Process subclasses + /// when they want to watch for a local process and have its exit + /// status automatically set when the host child process exits. + /// Subclasses should call Host::StartMonitoringChildProcess () + /// with: + /// callback = Process::SetHostProcessExitStatus + /// callback_baton = NULL + /// pid = Process::GetID() + /// monitor_signals = false + //------------------------------------------------------------------ + static bool + SetProcessExitStatus (void *callback_baton, // The callback baton which should be set to NULL + lldb::pid_t pid, // The process ID we want to monitor + bool exited, + int signo, // Zero for no signal + int status); // Exit value of process if signal is zero + + lldb::ByteOrder + GetByteOrder () const; + + uint32_t + GetAddressByteSize () const; + + uint32_t + GetUniqueID() const + { + return m_process_unique_id; + } + //------------------------------------------------------------------ + /// Check if a plug-in instance can debug the file in \a module. + /// + /// Each plug-in is given a chance to say whether it can debug + /// the file in \a module. If the Process plug-in instance can + /// debug a file on the current system, it should return \b true. + /// + /// @return + /// Returns \b true if this Process plug-in instance can + /// debug the executable, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + CanDebug (Target &target, + bool plugin_specified_by_name) = 0; + + + //------------------------------------------------------------------ + /// This object is about to be destroyed, do any necessary cleanup. + /// + /// Subclasses that override this method should always call this + /// superclass method. + //------------------------------------------------------------------ + virtual void + Finalize(); + + + //------------------------------------------------------------------ + /// Return whether this object is valid (i.e. has not been finalized.) + /// + /// @return + /// Returns \b true if this Process has not been finalized + /// and \b false otherwise. + //------------------------------------------------------------------ + bool + IsValid() const + { + return !m_finalize_called; + } + + //------------------------------------------------------------------ + /// Return a multi-word command object that can be used to expose + /// plug-in specific commands. + /// + /// This object will be used to resolve plug-in commands and can be + /// triggered by a call to: + /// + /// (lldb) process commmand + /// + /// @return + /// A CommandObject which can be one of the concrete subclasses + /// of CommandObject like CommandObjectRaw, CommandObjectParsed, + /// or CommandObjectMultiword. + //------------------------------------------------------------------ + virtual CommandObject * + GetPluginCommandObject() + { + return NULL; + } + + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using the + /// target object's executable module's file as the file to launch. + /// Arguments are given in \a argv, and the environment variables + /// are in \a envp. Standard input and output files can be + /// optionally re-directed to \a stdin_path, \a stdout_path, and + /// \a stderr_path. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call Process::WillLaunch (Module *) + /// and if that returns \b true, Process::DoLaunch (Module*, + /// char const *[],char const *[],const char *,const char *, + /// const char *) will be called to actually do the launching. If + /// DoLaunch returns \b true, then Process::DidLaunch() will be + /// called. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @return + /// An error object. Call GetID() to get the process ID if + /// the error object is success. + //------------------------------------------------------------------ + virtual Error + Launch (const ProcessLaunchInfo &launch_info); + + virtual Error + LoadCore (); + + virtual Error + DoLoadCore () + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support loading core files.", GetPluginName().GetCString()); + return error; + } + + //------------------------------------------------------------------ + /// Get the dynamic loader plug-in for this process. + /// + /// The default action is to let the DynamicLoader plug-ins check + /// the main executable and the DynamicLoader will select itself + /// automatically. Subclasses can override this if inspecting the + /// executable is not desired, or if Process subclasses can only + /// use a specific DynamicLoader plug-in. + //------------------------------------------------------------------ + virtual DynamicLoader * + GetDynamicLoader (); + + //------------------------------------------------------------------ + /// Attach to an existing process using the process attach info. + /// + /// This function is not meant to be overridden by Process + /// subclasses. It will first call WillAttach (lldb::pid_t) + /// or WillAttach (const char *), and if that returns \b + /// true, DoAttach (lldb::pid_t) or DoAttach (const char *) will + /// be called to actually do the attach. If DoAttach returns \b + /// true, then Process::DidAttach() will be called. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + Attach (ProcessAttachInfo &attach_info); + + //------------------------------------------------------------------ + /// Attach to a remote system via a URL + /// + /// @param[in] strm + /// A stream where output intended for the user + /// (if the driver has a way to display that) generated during + /// the connection. This may be NULL if no output is needed.A + /// + /// @param[in] remote_url + /// The URL format that we are connecting to. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + ConnectRemote (Stream *strm, const char *remote_url); + + bool + GetShouldDetach () const + { + return m_should_detach; + } + + void + SetShouldDetach (bool b) + { + m_should_detach = b; + } + + //------------------------------------------------------------------ + /// Get the image information address for the current process. + /// + /// Some runtimes have system functions that can help dynamic + /// loaders locate the dynamic loader information needed to observe + /// shared libraries being loaded or unloaded. This function is + /// in the Process interface (as opposed to the DynamicLoader + /// interface) to ensure that remote debugging can take advantage of + /// this functionality. + /// + /// @return + /// The address of the dynamic loader information, or + /// LLDB_INVALID_ADDRESS if this is not supported by this + /// interface. + //------------------------------------------------------------------ + virtual lldb::addr_t + GetImageInfoAddress (); + + //------------------------------------------------------------------ + /// Load a shared library into this process. + /// + /// Try and load a shared library into the current process. This + /// call might fail in the dynamic loader plug-in says it isn't safe + /// to try and load shared libraries at the moment. + /// + /// @param[in] image_spec + /// The image file spec that points to the shared library that + /// you want to load. + /// + /// @param[out] error + /// An error object that gets filled in with any errors that + /// might occur when trying to load the shared library. + /// + /// @return + /// A token that represents the shared library that can be + /// later used to unload the shared library. A value of + /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared + /// library can't be opened. + //------------------------------------------------------------------ + virtual uint32_t + LoadImage (const FileSpec &image_spec, Error &error); + + virtual Error + UnloadImage (uint32_t image_token); + + //------------------------------------------------------------------ + /// Register for process and thread notifications. + /// + /// Clients can register nofication callbacks by filling out a + /// Process::Notifications structure and calling this function. + /// + /// @param[in] callbacks + /// A structure that contains the notification baton and + /// callback functions. + /// + /// @see Process::Notifications + //------------------------------------------------------------------ +#ifndef SWIG + void + RegisterNotificationCallbacks (const Process::Notifications& callbacks); +#endif + //------------------------------------------------------------------ + /// Unregister for process and thread notifications. + /// + /// Clients can unregister nofication callbacks by passing a copy of + /// the original baton and callbacks in \a callbacks. + /// + /// @param[in] callbacks + /// A structure that contains the notification baton and + /// callback functions. + /// + /// @return + /// Returns \b true if the notification callbacks were + /// successfully removed from the process, \b false otherwise. + /// + /// @see Process::Notifications + //------------------------------------------------------------------ +#ifndef SWIG + bool + UnregisterNotificationCallbacks (const Process::Notifications& callbacks); +#endif + //================================================================== + // Built in Process Control functions + //================================================================== + //------------------------------------------------------------------ + /// Resumes all of a process's threads as configured using the + /// Thread run control functions. + /// + /// Threads for a process should be updated with one of the run + /// control actions (resume, step, or suspend) that they should take + /// when the process is resumed. If no run control action is given + /// to a thread it will be resumed by default. + /// + /// This function is not meant to be overridden by Process + /// subclasses. This function will take care of disabling any + /// breakpoints that threads may be stopped at, single stepping, and + /// re-enabling breakpoints, and enabling the basic flow control + /// that the plug-in instances need not worry about. + /// + /// N.B. This function also sets the Write side of the Run Lock, + /// which is unset when the corresponding stop event is pulled off + /// the Public Event Queue. If you need to resume the process without + /// setting the Run Lock, use PrivateResume (though you should only do + /// that from inside the Process class. + /// + /// @return + /// Returns an error object. + /// + /// @see Thread:Resume() + /// @see Thread:Step() + /// @see Thread:Suspend() + //------------------------------------------------------------------ + Error + Resume(); + + //------------------------------------------------------------------ + /// Halts a running process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// If the process is successfully halted, a eStateStopped + /// process event with GetInterrupted will be broadcast. If false, we will + /// halt the process with no events generated by the halt. + /// + /// @param[in] clear_thread_plans + /// If true, when the process stops, clear all thread plans. + /// + /// @return + /// Returns an error object. If the error is empty, the process is halted. + /// otherwise the halt has failed. + //------------------------------------------------------------------ + Error + Halt (bool clear_thread_plans = false); + + //------------------------------------------------------------------ + /// Detaches from a running or stopped process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @param[in] keep_stopped + /// If true, don't resume the process on detach. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + Error + Detach (bool keep_stopped); + + //------------------------------------------------------------------ + /// Kills the process and shuts down all threads that were spawned + /// to track and monitor the process. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + Error + Destroy(); + + //------------------------------------------------------------------ + /// Sends a process a UNIX signal \a signal. + /// + /// This function is not meant to be overridden by Process + /// subclasses. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + Error + Signal (int signal); + + virtual UnixSignals & + GetUnixSignals () + { + return m_unix_signals; + } + + //================================================================== + // Plug-in Process Control Overrides + //================================================================== + + //------------------------------------------------------------------ + /// Called before attaching to a process. + /// + /// Allow Process plug-ins to execute some code before attaching a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillAttachToProcessWithID (lldb::pid_t pid) + { + return Error(); + } + + //------------------------------------------------------------------ + /// Called before attaching to a process. + /// + /// Allow Process plug-ins to execute some code before attaching a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillAttachToProcessWithName (const char *process_name, bool wait_for_launch) + { + return Error(); + } + + //------------------------------------------------------------------ + /// Attach to a remote system via a URL + /// + /// @param[in] strm + /// A stream where output intended for the user + /// (if the driver has a way to display that) generated during + /// the connection. This may be NULL if no output is needed.A + /// + /// @param[in] remote_url + /// The URL format that we are connecting to. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + DoConnectRemote (Stream *strm, const char *remote_url) + { + Error error; + error.SetErrorString ("remote connections are not supported"); + return error; + } + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + DoAttachToProcessWithID (lldb::pid_t pid) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); + return error; + } + + //------------------------------------------------------------------ + /// Attach to an existing process using a process ID. + /// + /// @param[in] pid + /// The process ID that we should attempt to attach to. + /// + /// @param[in] attach_info + /// Information on how to do the attach. For example, GetUserID() + /// will return the uid to attach as. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + /// hanming : need flag + //------------------------------------------------------------------ + virtual Error + DoAttachToProcessWithID (lldb::pid_t pid, const ProcessAttachInfo &attach_info) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support attaching to a process by pid", GetPluginName().GetCString()); + return error; + } + + //------------------------------------------------------------------ + /// Attach to an existing process using a partial process name. + /// + /// @param[in] process_name + /// The name of the process to attach to. + /// + /// @param[in] wait_for_launch + /// If \b true, wait for the process to be launched and attach + /// as soon as possible after it does launch. If \b false, then + /// search for a matching process the currently exists. + /// + /// @param[in] attach_info + /// Information on how to do the attach. For example, GetUserID() + /// will return the uid to attach as. + /// + /// @return + /// Returns \a pid if attaching was successful, or + /// LLDB_INVALID_PROCESS_ID if attaching fails. + //------------------------------------------------------------------ + virtual Error + DoAttachToProcessWithName (const char *process_name, bool wait_for_launch, const ProcessAttachInfo &attach_info) + { + Error error; + error.SetErrorString("attach by name is not supported"); + return error; + } + + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow Process plug-ins to execute some code after attaching to + /// a process. + //------------------------------------------------------------------ + virtual void + DidAttach () {} + + + //------------------------------------------------------------------ + /// Called after a process re-execs itself. + /// + /// Allow Process plug-ins to execute some code after a process has + /// exec'ed itself. Subclasses typically should override DoDidExec() + /// as the lldb_private::Process class needs to remove its dynamic + /// loader, runtime, ABI and other plug-ins, as well as unload all + /// shared libraries. + //------------------------------------------------------------------ + virtual void + DidExec (); + + //------------------------------------------------------------------ + /// Subclasses of Process should implement this function if they + /// need to do anything after a process exec's itself. + //------------------------------------------------------------------ + virtual void + DoDidExec () + { + } + + //------------------------------------------------------------------ + /// Called before launching to a process. + /// + /// Allow Process plug-ins to execute some code before launching a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillLaunch (Module* module) + { + return Error(); + } + + //------------------------------------------------------------------ + /// Launch a new process. + /// + /// Launch a new process by spawning a new process using \a module's + /// file as the file to launch. Arguments are given in \a argv, + /// and the environment variables are in \a envp. Standard input + /// and output files can be optionally re-directed to \a stdin_path, + /// \a stdout_path, and \a stderr_path. + /// + /// @param[in] module + /// The module from which to extract the file specification and + /// launch. + /// + /// @param[in] argv + /// The argument array. + /// + /// @param[in] envp + /// The environment array. + /// + /// @param[in] launch_flags + /// Flags to modify the launch (@see lldb::LaunchFlags) + /// + /// @param[in] stdin_path + /// The path to use when re-directing the STDIN of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stdout_path + /// The path to use when re-directing the STDOUT of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] stderr_path + /// The path to use when re-directing the STDERR of the new + /// process. If all stdXX_path arguments are NULL, a pseudo + /// terminal will be used. + /// + /// @param[in] working_directory + /// The working directory to have the child process run in + /// + /// @return + /// A new valid process ID, or LLDB_INVALID_PROCESS_ID if + /// launching fails. + //------------------------------------------------------------------ + virtual Error + DoLaunch (Module *exe_module, + const ProcessLaunchInfo &launch_info) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support launching processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after launching a process. + /// + /// Allow Process plug-ins to execute some code after launching + /// a process. + //------------------------------------------------------------------ + virtual void + DidLaunch () {} + + + + //------------------------------------------------------------------ + /// Called before resuming to a process. + /// + /// Allow Process plug-ins to execute some code before resuming a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillResume () { return Error(); } + + //------------------------------------------------------------------ + /// Resumes all of a process's threads as configured using the + /// Thread run control functions. + /// + /// Threads for a process should be updated with one of the run + /// control actions (resume, step, or suspend) that they should take + /// when the process is resumed. If no run control action is given + /// to a thread it will be resumed by default. + /// + /// @return + /// Returns \b true if the process successfully resumes using + /// the thread run control actions, \b false otherwise. + /// + /// @see Thread:Resume() + /// @see Thread:Step() + /// @see Thread:Suspend() + //------------------------------------------------------------------ + virtual Error + DoResume () + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support resuming processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after resuming a process. + /// + /// Allow Process plug-ins to execute some code after resuming + /// a process. + //------------------------------------------------------------------ + virtual void + DidResume () {} + + + //------------------------------------------------------------------ + /// Called before halting to a process. + /// + /// Allow Process plug-ins to execute some code before halting a + /// process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillHalt () { return Error(); } + + //------------------------------------------------------------------ + /// Halts a running process. + /// + /// DoHalt must produce one and only one stop StateChanged event if it actually + /// stops the process. If the stop happens through some natural event (for + /// instance a SIGSTOP), then forwarding that event will do. Otherwise, you must + /// generate the event manually. Note also, the private event thread is stopped when + /// DoHalt is run to prevent the events generated while halting to trigger + /// other state changes before the halt is complete. + /// + /// @param[out] caused_stop + /// If true, then this Halt caused the stop, otherwise, the + /// process was already stopped. + /// + /// @return + /// Returns \b true if the process successfully halts, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual Error + DoHalt (bool &caused_stop) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support halting processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after halting a process. + /// + /// Allow Process plug-ins to execute some code after halting + /// a process. + //------------------------------------------------------------------ + virtual void + DidHalt () {} + + //------------------------------------------------------------------ + /// Called before detaching from a process. + /// + /// Allow Process plug-ins to execute some code before detaching + /// from a process. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + WillDetach () + { + return Error(); + } + + //------------------------------------------------------------------ + /// Detaches from a running or stopped process. + /// + /// @return + /// Returns \b true if the process successfully detaches, \b + /// false otherwise. + //------------------------------------------------------------------ + virtual Error + DoDetach (bool keep_stopped) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support detaching from processes", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// Called after detaching from a process. + /// + /// Allow Process plug-ins to execute some code after detaching + /// from a process. + //------------------------------------------------------------------ + virtual void + DidDetach () {} + + virtual bool + DetachRequiresHalt() { return false; } + + //------------------------------------------------------------------ + /// Called before sending a signal to a process. + /// + /// Allow Process plug-ins to execute some code before sending a + /// signal to a process. + /// + /// @return + /// Returns no error if it is safe to proceed with a call to + /// Process::DoSignal(int), otherwise an error describing what + /// prevents the signal from being sent. + //------------------------------------------------------------------ + virtual Error + WillSignal () { return Error(); } + + //------------------------------------------------------------------ + /// Sends a process a UNIX signal \a signal. + /// + /// @return + /// Returns an error object. + //------------------------------------------------------------------ + virtual Error + DoSignal (int signal) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support senging signals to processes", GetPluginName().GetCString()); + return error; + } + + virtual Error + WillDestroy () { return Error(); } + + virtual Error + DoDestroy () = 0; + + virtual void + DidDestroy () { } + + virtual bool + DestroyRequiresHalt() { return true; } + + + //------------------------------------------------------------------ + /// Called after sending a signal to a process. + /// + /// Allow Process plug-ins to execute some code after sending a + /// signal to a process. + //------------------------------------------------------------------ + virtual void + DidSignal () {} + + //------------------------------------------------------------------ + /// Currently called as part of ShouldStop. + /// FIXME: Should really happen when the target stops before the + /// event is taken from the queue... + /// + /// This callback is called as the event + /// is about to be queued up to allow Process plug-ins to execute + /// some code prior to clients being notified that a process was + /// stopped. Common operations include updating the thread list, + /// invalidating any thread state (registers, stack, etc) prior to + /// letting the notification go out. + /// + //------------------------------------------------------------------ + virtual void + RefreshStateAfterStop () = 0; + + //------------------------------------------------------------------ + /// Get the target object pointer for this module. + /// + /// @return + /// A Target object pointer to the target that owns this + /// module. + //------------------------------------------------------------------ + Target & + GetTarget () + { + return m_target; + } + + //------------------------------------------------------------------ + /// Get the const target object pointer for this module. + /// + /// @return + /// A const Target object pointer to the target that owns this + /// module. + //------------------------------------------------------------------ + const Target & + GetTarget () const + { + return m_target; + } + + //------------------------------------------------------------------ + /// Flush all data in the process. + /// + /// Flush the memory caches, all threads, and any other cached data + /// in the process. + /// + /// This function can be called after a world changing event like + /// adding a new symbol file, or after the process makes a large + /// context switch (from boot ROM to booted into an OS). + //------------------------------------------------------------------ + void + Flush (); + + //------------------------------------------------------------------ + /// Get accessor for the current process state. + /// + /// @return + /// The current state of the process. + /// + /// @see lldb::StateType + //------------------------------------------------------------------ + lldb::StateType + GetState (); + + ExecutionResults + RunThreadPlan (ExecutionContext &exe_ctx, + lldb::ThreadPlanSP &thread_plan_sp, + bool stop_others, + bool run_others, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors); + + static const char * + ExecutionResultAsCString (ExecutionResults result); + + void + GetStatus (Stream &ostrm); + + size_t + GetThreadStatus (Stream &ostrm, + bool only_threads_with_stop_reason, + uint32_t start_frame, + uint32_t num_frames, + uint32_t num_frames_with_source); + + void + SendAsyncInterrupt (); + +protected: + + void + SetState (lldb::EventSP &event_sp); + + lldb::StateType + GetPrivateState (); + + //------------------------------------------------------------------ + /// The "private" side of resuming a process. This doesn't alter the + /// state of m_run_lock, but just causes the process to resume. + /// + /// @return + /// An Error object describing the success or failure of the resume. + //------------------------------------------------------------------ + Error + PrivateResume (); + + //------------------------------------------------------------------ + // Called internally + //------------------------------------------------------------------ + void + CompleteAttach (); + +public: + //------------------------------------------------------------------ + /// Get the exit status for a process. + /// + /// @return + /// The process's return code, or -1 if the current process + /// state is not eStateExited. + //------------------------------------------------------------------ + int + GetExitStatus (); + + //------------------------------------------------------------------ + /// Get a textual description of what the process exited. + /// + /// @return + /// The textual description of why the process exited, or NULL + /// if there is no description available. + //------------------------------------------------------------------ + const char * + GetExitDescription (); + + + virtual void + DidExit () + { + } + + //------------------------------------------------------------------ + /// Get the Modification ID of the process. + /// + /// @return + /// The modification ID of the process. + //------------------------------------------------------------------ + ProcessModID + GetModID () const + { + return m_mod_id; + } + + const ProcessModID & + GetModIDRef () const + { + return m_mod_id; + } + + uint32_t + GetStopID () const + { + return m_mod_id.GetStopID(); + } + + uint32_t + GetResumeID () const + { + return m_mod_id.GetResumeID(); + } + + uint32_t + GetLastUserExpressionResumeID () const + { + return m_mod_id.GetLastUserExpressionResumeID(); + } + + uint32_t + GetLastNaturalStopID() + { + return m_mod_id.GetLastNaturalStopID(); + } + + //------------------------------------------------------------------ + /// Set accessor for the process exit status (return code). + /// + /// Sometimes a child exits and the exit can be detected by global + /// functions (signal handler for SIGCHLD for example). This + /// accessor allows the exit status to be set from an external + /// source. + /// + /// Setting this will cause a eStateExited event to be posted to + /// the process event queue. + /// + /// @param[in] exit_status + /// The value for the process's return code. + /// + /// @see lldb::StateType + //------------------------------------------------------------------ + virtual bool + SetExitStatus (int exit_status, const char *cstr); + + //------------------------------------------------------------------ + /// Check if a process is still alive. + /// + /// @return + /// Returns \b true if the process is still valid, \b false + /// otherwise. + //------------------------------------------------------------------ + virtual bool + IsAlive () = 0; + + //------------------------------------------------------------------ + /// Before lldb detaches from a process, it warns the user that they are about to lose their debug session. + /// In some cases, this warning doesn't need to be emitted -- for instance, with core file debugging where + /// the user can reconstruct the "state" by simply re-running the debugger on the core file. + /// + /// @return + // true if the user should be warned about detaching from this process. + //------------------------------------------------------------------ + virtual bool + WarnBeforeDetach () const + { + return true; + } + + //------------------------------------------------------------------ + /// Actually do the reading of memory from a process. + /// + /// Subclasses must override this function and can return fewer + /// bytes than requested when memory requests are too large. This + /// class will break up the memory requests and keep advancing the + /// arguments along as needed. + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start reading + /// memory from. + /// + /// @param[in] size + /// The number of bytes to read. + /// + /// @param[out] buf + /// A byte buffer that is at least \a size bytes long that + /// will receive the memory bytes. + /// + /// @return + /// The number of bytes that were actually read into \a buf. + //------------------------------------------------------------------ + virtual size_t + DoReadMemory (lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error) = 0; + + //------------------------------------------------------------------ + /// Read of memory from a process. + /// + /// This function will read memory from the current process's + /// address space and remove any traps that may have been inserted + /// into the memory. + /// + /// This function is not meant to be overridden by Process + /// subclasses, the subclasses should implement + /// Process::DoReadMemory (lldb::addr_t, size_t, void *). + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start reading + /// memory from. + /// + /// @param[out] buf + /// A byte buffer that is at least \a size bytes long that + /// will receive the memory bytes. + /// + /// @param[in] size + /// The number of bytes to read. + /// + /// @return + /// The number of bytes that were actually read into \a buf. If + /// the returned number is greater than zero, yet less than \a + /// size, then this function will get called again with \a + /// vm_addr, \a buf, and \a size updated appropriately. Zero is + /// returned to indicate an error. + //------------------------------------------------------------------ + virtual size_t + ReadMemory (lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error); + + //------------------------------------------------------------------ + /// Read a NULL terminated string from memory + /// + /// This function will read a cache page at a time until a NULL + /// string terminator is found. It will stop reading if an aligned + /// sequence of NULL termination \a type_width bytes is not found + /// before reading \a cstr_max_len bytes. The results are always + /// guaranteed to be NULL terminated, and that no more than + /// (max_bytes - type_width) bytes will be read. + /// + /// @param[in] vm_addr + /// The virtual load address to start the memory read. + /// + /// @param[in] str + /// A character buffer containing at least max_bytes. + /// + /// @param[in] max_bytes + /// The maximum number of bytes to read. + /// + /// @param[in] error + /// The error status of the read operation. + /// + /// @param[in] type_width + /// The size of the null terminator (1 to 4 bytes per + /// character). Defaults to 1. + /// + /// @return + /// The error status or the number of bytes prior to the null terminator. + //------------------------------------------------------------------ + size_t + ReadStringFromMemory (lldb::addr_t vm_addr, + char *str, + size_t max_bytes, + Error &error, + size_t type_width = 1); + + //------------------------------------------------------------------ + /// Read a NULL terminated C string from memory + /// + /// This function will read a cache page at a time until the NULL + /// C string terminator is found. It will stop reading if the NULL + /// termination byte isn't found before reading \a cstr_max_len + /// bytes, and the results are always guaranteed to be NULL + /// terminated (at most cstr_max_len - 1 bytes will be read). + //------------------------------------------------------------------ + size_t + ReadCStringFromMemory (lldb::addr_t vm_addr, + char *cstr, + size_t cstr_max_len, + Error &error); + + size_t + ReadCStringFromMemory (lldb::addr_t vm_addr, + std::string &out_str, + Error &error); + + size_t + ReadMemoryFromInferior (lldb::addr_t vm_addr, + void *buf, + size_t size, + Error &error); + + //------------------------------------------------------------------ + /// Reads an unsigned integer of the specified byte size from + /// process memory. + /// + /// @param[in] load_addr + /// A load address of the integer to read. + /// + /// @param[in] byte_size + /// The size in byte of the integer to read. + /// + /// @param[in] fail_value + /// The value to return if we fail to read an integer. + /// + /// @param[out] error + /// An error that indicates the success or failure of this + /// operation. If error indicates success (error.Success()), + /// then the value returned can be trusted, otherwise zero + /// will be returned. + /// + /// @return + /// The unsigned integer that was read from the process memory + /// space. If the integer was smaller than a uint64_t, any + /// unused upper bytes will be zero filled. If the process + /// byte order differs from the host byte order, the integer + /// value will be appropriately byte swapped into host byte + /// order. + //------------------------------------------------------------------ + uint64_t + ReadUnsignedIntegerFromMemory (lldb::addr_t load_addr, + size_t byte_size, + uint64_t fail_value, + Error &error); + + lldb::addr_t + ReadPointerFromMemory (lldb::addr_t vm_addr, + Error &error); + + bool + WritePointerToMemory (lldb::addr_t vm_addr, + lldb::addr_t ptr_value, + Error &error); + + //------------------------------------------------------------------ + /// Actually do the writing of memory to a process. + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start writing + /// memory to. + /// + /// @param[in] buf + /// A byte buffer that is at least \a size bytes long that + /// contains the data to write. + /// + /// @param[in] size + /// The number of bytes to write. + /// + /// @param[out] error + /// An error value in case the memory write fails. + /// + /// @return + /// The number of bytes that were actually written. + //------------------------------------------------------------------ + virtual size_t + DoWriteMemory (lldb::addr_t vm_addr, const void *buf, size_t size, Error &error) + { + error.SetErrorStringWithFormat("error: %s does not support writing to processes", GetPluginName().GetCString()); + return 0; + } + + + //------------------------------------------------------------------ + /// Write all or part of a scalar value to memory. + /// + /// The value contained in \a scalar will be swapped to match the + /// byte order of the process that is being debugged. If \a size is + /// less than the size of scalar, the least significate \a size bytes + /// from scalar will be written. If \a size is larger than the byte + /// size of scalar, then the extra space will be padded with zeros + /// and the scalar value will be placed in the least significant + /// bytes in memory. + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start writing + /// memory to. + /// + /// @param[in] scalar + /// The scalar to write to the debugged process. + /// + /// @param[in] size + /// This value can be smaller or larger than the scalar value + /// itself. If \a size is smaller than the size of \a scalar, + /// the least significant bytes in \a scalar will be used. If + /// \a size is larger than the byte size of \a scalar, then + /// the extra space will be padded with zeros. If \a size is + /// set to UINT32_MAX, then the size of \a scalar will be used. + /// + /// @param[out] error + /// An error value in case the memory write fails. + /// + /// @return + /// The number of bytes that were actually written. + //------------------------------------------------------------------ + size_t + WriteScalarToMemory (lldb::addr_t vm_addr, + const Scalar &scalar, + size_t size, + Error &error); + + size_t + ReadScalarIntegerFromMemory (lldb::addr_t addr, + uint32_t byte_size, + bool is_signed, + Scalar &scalar, + Error &error); + + //------------------------------------------------------------------ + /// Write memory to a process. + /// + /// This function will write memory to the current process's + /// address space and maintain any traps that might be present due + /// to software breakpoints. + /// + /// This function is not meant to be overridden by Process + /// subclasses, the subclasses should implement + /// Process::DoWriteMemory (lldb::addr_t, size_t, void *). + /// + /// @param[in] vm_addr + /// A virtual load address that indicates where to start writing + /// memory to. + /// + /// @param[in] buf + /// A byte buffer that is at least \a size bytes long that + /// contains the data to write. + /// + /// @param[in] size + /// The number of bytes to write. + /// + /// @return + /// The number of bytes that were actually written. + //------------------------------------------------------------------ + size_t + WriteMemory (lldb::addr_t vm_addr, const void *buf, size_t size, Error &error); + + + //------------------------------------------------------------------ + /// Actually allocate memory in the process. + /// + /// This function will allocate memory in the process's address + /// space. This can't rely on the generic function calling mechanism, + /// since that requires this function. + /// + /// @param[in] size + /// The size of the allocation requested. + /// + /// @return + /// The address of the allocated buffer in the process, or + /// LLDB_INVALID_ADDRESS if the allocation failed. + //------------------------------------------------------------------ + + virtual lldb::addr_t + DoAllocateMemory (size_t size, uint32_t permissions, Error &error) + { + error.SetErrorStringWithFormat("error: %s does not support allocating in the debug process", GetPluginName().GetCString()); + return LLDB_INVALID_ADDRESS; + } + + + //------------------------------------------------------------------ + /// The public interface to allocating memory in the process. + /// + /// This function will allocate memory in the process's address + /// space. This can't rely on the generic function calling mechanism, + /// since that requires this function. + /// + /// @param[in] size + /// The size of the allocation requested. + /// + /// @param[in] permissions + /// Or together any of the lldb::Permissions bits. The permissions on + /// a given memory allocation can't be changed after allocation. Note + /// that a block that isn't set writable can still be written on from lldb, + /// just not by the process itself. + /// + /// @param[in/out] error + /// An error object to fill in if things go wrong. + /// @return + /// The address of the allocated buffer in the process, or + /// LLDB_INVALID_ADDRESS if the allocation failed. + //------------------------------------------------------------------ + + lldb::addr_t + AllocateMemory (size_t size, uint32_t permissions, Error &error); + + + //------------------------------------------------------------------ + /// Resolve dynamically loaded indirect functions. + /// + /// @param[in] address + /// The load address of the indirect function to resolve. + /// + /// @param[out] error + /// An error value in case the resolve fails. + /// + /// @return + /// The address of the resolved function. + /// LLDB_INVALID_ADDRESS if the resolution failed. + //------------------------------------------------------------------ + + virtual lldb::addr_t + ResolveIndirectFunction(const Address *address, Error &error) + { + error.SetErrorStringWithFormat("error: %s does not support indirect functions in the debug process", GetPluginName().GetCString()); + return LLDB_INVALID_ADDRESS; + } + + virtual Error + GetMemoryRegionInfo (lldb::addr_t load_addr, + MemoryRegionInfo &range_info) + { + Error error; + error.SetErrorString ("Process::GetMemoryRegionInfo() not supported"); + return error; + } + + virtual Error + GetWatchpointSupportInfo (uint32_t &num) + { + Error error; + num = 0; + error.SetErrorString ("Process::GetWatchpointSupportInfo() not supported"); + return error; + } + + virtual Error + GetWatchpointSupportInfo (uint32_t &num, bool& after) + { + Error error; + num = 0; + after = true; + error.SetErrorString ("Process::GetWatchpointSupportInfo() not supported"); + return error; + } + + lldb::ModuleSP + ReadModuleFromMemory (const FileSpec& file_spec, + lldb::addr_t header_addr); + + //------------------------------------------------------------------ + /// Attempt to get the attributes for a region of memory in the process. + /// + /// It may be possible for the remote debug server to inspect attributes + /// for a region of memory in the process, such as whether there is a + /// valid page of memory at a given address or whether that page is + /// readable/writable/executable by the process. + /// + /// @param[in] load_addr + /// The address of interest in the process. + /// + /// @param[out] permissions + /// If this call returns successfully, this bitmask will have + /// its Permissions bits set to indicate whether the region is + /// readable/writable/executable. If this call fails, the + /// bitmask values are undefined. + /// + /// @return + /// Returns true if it was able to determine the attributes of the + /// memory region. False if not. + //------------------------------------------------------------------ + + virtual bool + GetLoadAddressPermissions (lldb::addr_t load_addr, uint32_t &permissions) + { + MemoryRegionInfo range_info; + permissions = 0; + Error 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; + } + + //------------------------------------------------------------------ + /// Determines whether executing JIT-compiled code in this process + /// is possible. + /// + /// @return + /// True if execution of JIT code is possible; false otherwise. + //------------------------------------------------------------------ + bool CanJIT (); + + //------------------------------------------------------------------ + /// Sets whether executing JIT-compiled code in this process + /// is possible. + /// + /// @param[in] can_jit + /// True if execution of JIT code is possible; false otherwise. + //------------------------------------------------------------------ + void SetCanJIT (bool can_jit); + + //------------------------------------------------------------------ + /// Actually deallocate memory in the process. + /// + /// This function will deallocate memory in the process's address + /// space that was allocated with AllocateMemory. + /// + /// @param[in] ptr + /// A return value from AllocateMemory, pointing to the memory you + /// want to deallocate. + /// + /// @return + /// \btrue if the memory was deallocated, \bfalse otherwise. + //------------------------------------------------------------------ + + virtual Error + DoDeallocateMemory (lldb::addr_t ptr) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support deallocating in the debug process", GetPluginName().GetCString()); + return error; + } + + + //------------------------------------------------------------------ + /// The public interface to deallocating memory in the process. + /// + /// This function will deallocate memory in the process's address + /// space that was allocated with AllocateMemory. + /// + /// @param[in] ptr + /// A return value from AllocateMemory, pointing to the memory you + /// want to deallocate. + /// + /// @return + /// \btrue if the memory was deallocated, \bfalse otherwise. + //------------------------------------------------------------------ + + Error + DeallocateMemory (lldb::addr_t ptr); + + //------------------------------------------------------------------ + /// Get any available STDOUT. + /// + /// If the process was launched without supplying valid file paths + /// for stdin, stdout, and stderr, then the Process class might + /// try to cache the STDOUT for the process if it is able. Events + /// will be queued indicating that there is STDOUT available that + /// can be retrieved using this function. + /// + /// @param[out] buf + /// A buffer that will receive any STDOUT bytes that are + /// currently available. + /// + /// @param[out] buf_size + /// The size in bytes for the buffer \a buf. + /// + /// @return + /// The number of bytes written into \a buf. If this value is + /// equal to \a buf_size, another call to this function should + /// be made to retrieve more STDOUT data. + //------------------------------------------------------------------ + virtual size_t + GetSTDOUT (char *buf, size_t buf_size, Error &error); + + //------------------------------------------------------------------ + /// Get any available STDERR. + /// + /// If the process was launched without supplying valid file paths + /// for stdin, stdout, and stderr, then the Process class might + /// try to cache the STDERR for the process if it is able. Events + /// will be queued indicating that there is STDERR available that + /// can be retrieved using this function. + /// + /// @param[out] buf + /// A buffer that will receive any STDERR bytes that are + /// currently available. + /// + /// @param[out] buf_size + /// The size in bytes for the buffer \a buf. + /// + /// @return + /// The number of bytes written into \a buf. If this value is + /// equal to \a buf_size, another call to this function should + /// be made to retrieve more STDERR data. + //------------------------------------------------------------------ + virtual size_t + GetSTDERR (char *buf, size_t buf_size, Error &error); + + virtual size_t + PutSTDIN (const char *buf, size_t buf_size, Error &error) + { + error.SetErrorString("stdin unsupported"); + return 0; + } + + //------------------------------------------------------------------ + /// Get any available profile data. + /// + /// @param[out] buf + /// A buffer that will receive any profile data bytes that are + /// currently available. + /// + /// @param[out] buf_size + /// The size in bytes for the buffer \a buf. + /// + /// @return + /// The number of bytes written into \a buf. If this value is + /// equal to \a buf_size, another call to this function should + /// be made to retrieve more profile data. + //------------------------------------------------------------------ + virtual size_t + GetAsyncProfileData (char *buf, size_t buf_size, Error &error); + + //---------------------------------------------------------------------- + // Process Breakpoints + //---------------------------------------------------------------------- + size_t + GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site); + + virtual Error + EnableBreakpointSite (BreakpointSite *bp_site) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support enabling breakpoints", GetPluginName().GetCString()); + return error; + } + + + virtual Error + DisableBreakpointSite (BreakpointSite *bp_site) + { + Error error; + error.SetErrorStringWithFormat("error: %s does not support disabling breakpoints", GetPluginName().GetCString()); + return error; + } + + + // This is implemented completely using the lldb::Process API. Subclasses + // don't need to implement this function unless the standard flow of + // read existing opcode, write breakpoint opcode, verify breakpoint opcode + // doesn't work for a specific process plug-in. + virtual Error + EnableSoftwareBreakpoint (BreakpointSite *bp_site); + + // This is implemented completely using the lldb::Process API. Subclasses + // don't need to implement this function unless the standard flow of + // restoring original opcode in memory and verifying the restored opcode + // doesn't work for a specific process plug-in. + virtual Error + DisableSoftwareBreakpoint (BreakpointSite *bp_site); + + BreakpointSiteList & + GetBreakpointSiteList(); + + const BreakpointSiteList & + GetBreakpointSiteList() const; + + void + DisableAllBreakpointSites (); + + Error + ClearBreakpointSiteByID (lldb::user_id_t break_id); + + lldb::break_id_t + CreateBreakpointSite (const lldb::BreakpointLocationSP &owner, + bool use_hardware); + + Error + DisableBreakpointSiteByID (lldb::user_id_t break_id); + + Error + EnableBreakpointSiteByID (lldb::user_id_t break_id); + + + // BreakpointLocations use RemoveOwnerFromBreakpointSite to remove + // themselves from the owner's list of this breakpoint sites. + void + RemoveOwnerFromBreakpointSite (lldb::user_id_t owner_id, + lldb::user_id_t owner_loc_id, + lldb::BreakpointSiteSP &bp_site_sp); + + //---------------------------------------------------------------------- + // Process Watchpoints (optional) + //---------------------------------------------------------------------- + virtual Error + EnableWatchpoint (Watchpoint *wp, bool notify = true); + + virtual Error + DisableWatchpoint (Watchpoint *wp, bool notify = true); + + //------------------------------------------------------------------ + // Thread Queries + //------------------------------------------------------------------ + virtual bool + UpdateThreadList (ThreadList &old_thread_list, ThreadList &new_thread_list) = 0; + + void + UpdateThreadListIfNeeded (); + + ThreadList & + GetThreadList () + { + return m_thread_list; + } + + uint32_t + GetNextThreadIndexID (uint64_t thread_id); + + lldb::ThreadSP + CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context); + + // Returns true if an index id has been assigned to a thread. + bool + HasAssignedIndexIDToThread(uint64_t sb_thread_id); + + // Given a thread_id, it will assign a more reasonable index id for display to the user. + // If the thread_id has previously been assigned, the same index id will be used. + uint32_t + AssignIndexIDToThread(uint64_t thread_id); + + //------------------------------------------------------------------ + // Event Handling + //------------------------------------------------------------------ + lldb::StateType + GetNextEvent (lldb::EventSP &event_sp); + + lldb::StateType + WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr = NULL); + + lldb::StateType + WaitForStateChangedEvents (const TimeValue *timeout, lldb::EventSP &event_sp); + + Event * + PeekAtStateChangedEvents (); + + + class + ProcessEventHijacker + { + public: + ProcessEventHijacker (Process &process, Listener *listener) : + m_process (process) + { + m_process.HijackProcessEvents (listener); + } + ~ProcessEventHijacker () + { + m_process.RestoreProcessEvents(); + } + + private: + Process &m_process; + }; + friend class ProcessEventHijacker; + //------------------------------------------------------------------ + /// If you need to ensure that you and only you will hear about some public + /// event, then make a new listener, set to listen to process events, and + /// then call this with that listener. Then you will have to wait on that + /// listener explicitly for events (rather than using the GetNextEvent & WaitFor* + /// calls above. Be sure to call RestoreProcessEvents when you are done. + /// + /// @param[in] listener + /// This is the new listener to whom all process events will be delivered. + /// + /// @return + /// Returns \b true if the new listener could be installed, + /// \b false otherwise. + //------------------------------------------------------------------ + bool + HijackProcessEvents (Listener *listener); + + //------------------------------------------------------------------ + /// Restores the process event broadcasting to its normal state. + /// + //------------------------------------------------------------------ + void + RestoreProcessEvents (); + +private: + //------------------------------------------------------------------ + /// This is the part of the event handling that for a process event. + /// It decides what to do with the event and returns true if the + /// event needs to be propagated to the user, and false otherwise. + /// If the event is not propagated, this call will most likely set + /// the target to executing again. + /// There is only one place where this call should be called, HandlePrivateEvent. + /// Don't call it from anywhere else... + /// + /// @param[in] event_ptr + /// This is the event we are handling. + /// + /// @return + /// Returns \b true if the event should be reported to the + /// user, \b false otherwise. + //------------------------------------------------------------------ + bool + ShouldBroadcastEvent (Event *event_ptr); + +public: + const lldb::ABISP & + GetABI (); + + OperatingSystem * + GetOperatingSystem () + { + return m_os_ap.get(); + } + + + virtual LanguageRuntime * + GetLanguageRuntime (lldb::LanguageType language, bool retry_if_null = true); + + virtual CPPLanguageRuntime * + GetCPPLanguageRuntime (bool retry_if_null = true); + + virtual ObjCLanguageRuntime * + GetObjCLanguageRuntime (bool retry_if_null = true); + + bool + IsPossibleDynamicValue (ValueObject& in_value); + + bool + IsRunning () const; + + DynamicCheckerFunctions *GetDynamicCheckers() + { + return m_dynamic_checkers_ap.get(); + } + + void SetDynamicCheckers(DynamicCheckerFunctions *dynamic_checkers) + { + m_dynamic_checkers_ap.reset(dynamic_checkers); + } + + //------------------------------------------------------------------ + /// Call this to set the lldb in the mode where it breaks on new thread + /// creations, and then auto-restarts. This is useful when you are trying + /// to run only one thread, but either that thread or the kernel is creating + /// new threads in the process. If you stop when the thread is created, you + /// can immediately suspend it, and keep executing only the one thread you intend. + /// + /// @return + /// Returns \b true if we were able to start up the notification + /// \b false otherwise. + //------------------------------------------------------------------ + virtual bool + StartNoticingNewThreads() + { + return true; + } + + //------------------------------------------------------------------ + /// Call this to turn off the stop & notice new threads mode. + /// + /// @return + /// Returns \b true if we were able to start up the notification + /// \b false otherwise. + //------------------------------------------------------------------ + virtual bool + StopNoticingNewThreads() + { + return true; + } + + void + SetRunningUserExpression (bool on); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess () + { + return shared_from_this(); + } + + virtual lldb::ThreadSP + CalculateThread () + { + return lldb::ThreadSP(); + } + + virtual lldb::StackFrameSP + CalculateStackFrame () + { + return lldb::StackFrameSP(); + } + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + void + SetSTDIOFileDescriptor (int file_descriptor); + + //------------------------------------------------------------------ + // Add a permanent region of memory that should never be read or + // written to. This can be used to ensure that memory reads or writes + // to certain areas of memory never end up being sent to the + // DoReadMemory or DoWriteMemory functions which can improve + // performance. + //------------------------------------------------------------------ + void + AddInvalidMemoryRegion (const LoadRange ®ion); + + //------------------------------------------------------------------ + // Remove a permanent region of memory that should never be read or + // written to that was previously added with AddInvalidMemoryRegion. + //------------------------------------------------------------------ + bool + RemoveInvalidMemoryRange (const LoadRange ®ion); + + //------------------------------------------------------------------ + // If the setup code of a thread plan needs to do work that might involve + // calling a function in the target, it should not do that work directly + // in one of the thread plan functions (DidPush/WillResume) because + // such work needs to be handled carefully. Instead, put that work in + // a PreResumeAction callback, and register it with the process. It will + // get done before the actual "DoResume" gets called. + //------------------------------------------------------------------ + + typedef bool (PreResumeActionCallback)(void *); + + void + AddPreResumeAction (PreResumeActionCallback callback, void *baton); + + bool + RunPreResumeActions (); + + void + ClearPreResumeActions (); + + ProcessRunLock & + GetRunLock () + { + if (Host::GetCurrentThread() == m_private_state_thread) + return m_private_run_lock; + else + return m_public_run_lock; + } + +protected: + //------------------------------------------------------------------ + // NextEventAction provides a way to register an action on the next + // event that is delivered to this process. There is currently only + // one next event action allowed in the process at one time. If a + // new "NextEventAction" is added while one is already present, the + // old action will be discarded (with HandleBeingUnshipped called + // after it is discarded.) + // + // If you want to resume the process as a result of a resume action, + // call RequestResume, don't call Resume directly. + //------------------------------------------------------------------ + class NextEventAction + { + public: + typedef enum EventActionResult + { + eEventActionSuccess, + eEventActionRetry, + eEventActionExit + } EventActionResult; + + NextEventAction (Process *process) : + m_process(process) + { + } + + virtual + ~NextEventAction() + { + } + + virtual EventActionResult PerformAction (lldb::EventSP &event_sp) = 0; + virtual void HandleBeingUnshipped () {} + virtual EventActionResult HandleBeingInterrupted () = 0; + virtual const char *GetExitString() = 0; + void RequestResume() + { + m_process->m_resume_requested = true; + } + protected: + Process *m_process; + }; + + void SetNextEventAction (Process::NextEventAction *next_event_action) + { + if (m_next_event_action_ap.get()) + m_next_event_action_ap->HandleBeingUnshipped(); + + m_next_event_action_ap.reset(next_event_action); + } + + // This is the completer for Attaching: + class AttachCompletionHandler : public NextEventAction + { + public: + AttachCompletionHandler (Process *process, uint32_t exec_count) : + NextEventAction (process), + m_exec_count (exec_count) + { + } + + virtual + ~AttachCompletionHandler() + { + } + + virtual EventActionResult PerformAction (lldb::EventSP &event_sp); + virtual EventActionResult HandleBeingInterrupted (); + virtual const char *GetExitString(); + private: + uint32_t m_exec_count; + std::string m_exit_string; + }; + + bool + HijackPrivateProcessEvents (Listener *listener); + + void + RestorePrivateProcessEvents (); + + bool + PrivateStateThreadIsValid () const + { + return IS_VALID_LLDB_HOST_THREAD(m_private_state_thread); + } + + //------------------------------------------------------------------ + // Type definitions + //------------------------------------------------------------------ + typedef std::map LanguageRuntimeCollection; + + struct PreResumeCallbackAndBaton + { + bool (*callback) (void *); + void *baton; + PreResumeCallbackAndBaton (PreResumeActionCallback in_callback, void *in_baton) : + callback (in_callback), + baton (in_baton) + { + } + }; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + Target & m_target; ///< The target that owns this process. + ThreadSafeValue m_public_state; + ThreadSafeValue m_private_state; // The actual state of our process + Broadcaster m_private_state_broadcaster; // This broadcaster feeds state changed events into the private state thread's listener. + Broadcaster m_private_state_control_broadcaster; // This is the control broadcaster, used to pause, resume & stop the private state thread. + Listener m_private_state_listener; // This is the listener for the private state thread. + Predicate m_private_state_control_wait; /// This Predicate is used to signal that a control operation is complete. + lldb::thread_t m_private_state_thread; // Thread ID for the thread that watches interal state events + ProcessModID m_mod_id; ///< Tracks the state of the process over stops and other alterations. + uint32_t m_process_unique_id; ///< Each lldb_private::Process class that is created gets a unique integer ID that increments with each new instance + uint32_t m_thread_index_id; ///< Each thread is created with a 1 based index that won't get re-used. + std::map m_thread_id_to_index_id_map; + int m_exit_status; ///< The exit status of the process, or -1 if not set. + std::string m_exit_string; ///< A textual description of why a process exited. + Mutex m_thread_mutex; + ThreadList m_thread_list_real; ///< The threads for this process as are known to the protocol we are debugging with + ThreadList m_thread_list; ///< The threads for this process as the user will see them. This is usually the same as + ///< m_thread_list_real, but might be different if there is an OS plug-in creating memory threads + std::vector m_notifications; ///< The list of notifications that this process can deliver. + std::vector m_image_tokens; + Listener &m_listener; + BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint locations we intend to insert in the target. + std::unique_ptr m_dyld_ap; + std::unique_ptr m_dynamic_checkers_ap; ///< The functions used by the expression parser to validate data that expressions use. + std::unique_ptr m_os_ap; + UnixSignals m_unix_signals; /// This is the current signal set for this process. + lldb::ABISP m_abi_sp; + lldb::InputReaderSP m_process_input_reader; + Communication m_stdio_communication; + Mutex m_stdio_communication_mutex; + std::string m_stdout_data; + std::string m_stderr_data; + Mutex m_profile_data_comm_mutex; + std::vector m_profile_data; + MemoryCache m_memory_cache; + AllocatedMemoryCache m_allocated_memory_cache; + bool m_should_detach; /// Should we detach if the process object goes away with an explicit call to Kill or Detach? + LanguageRuntimeCollection m_language_runtimes; + std::unique_ptr m_next_event_action_ap; + std::vector m_pre_resume_actions; + ProcessRunLock m_public_run_lock; + ProcessRunLock m_private_run_lock; + Predicate m_currently_handling_event; // This predicate is set in HandlePrivateEvent while all its business is being done. + bool m_currently_handling_do_on_removals; + bool m_resume_requested; // If m_currently_handling_event or m_currently_handling_do_on_removals are true, Resume will only request a resume, using this flag to check. + bool m_finalize_called; + bool m_clear_thread_plans_on_stop; + lldb::StateType m_last_broadcast_state; /// This helps with the Public event coalescing in ShouldBroadcastEvent. + bool m_destroy_in_process; + + enum { + eCanJITDontKnow= 0, + eCanJITYes, + eCanJITNo + } m_can_jit; + + size_t + RemoveBreakpointOpcodesFromBuffer (lldb::addr_t addr, size_t size, uint8_t *buf) const; + + void + SynchronouslyNotifyStateChanged (lldb::StateType state); + + void + SetPublicState (lldb::StateType new_state, bool restarted); + + void + SetPrivateState (lldb::StateType state); + + bool + StartPrivateStateThread (bool force = false); + + void + StopPrivateStateThread (); + + void + PausePrivateStateThread (); + + void + ResumePrivateStateThread (); + + static void * + PrivateStateThread (void *arg); + + void * + RunPrivateStateThread (); + + void + HandlePrivateEvent (lldb::EventSP &event_sp); + + lldb::StateType + WaitForProcessStopPrivate (const TimeValue *timeout, lldb::EventSP &event_sp); + + // This waits for both the state change broadcaster, and the control broadcaster. + // If control_only, it only waits for the control broadcaster. + + bool + WaitForEventsPrivate (const TimeValue *timeout, lldb::EventSP &event_sp, bool control_only); + + lldb::StateType + WaitForStateChangedEventsPrivate (const TimeValue *timeout, lldb::EventSP &event_sp); + + lldb::StateType + WaitForState (const TimeValue *timeout, + const lldb::StateType *match_states, + const uint32_t num_match_states); + + size_t + WriteMemoryPrivate (lldb::addr_t addr, const void *buf, size_t size, Error &error); + + void + AppendSTDOUT (const char *s, size_t len); + + void + AppendSTDERR (const char *s, size_t len); + + void + BroadcastAsyncProfileData(const std::string &one_profile_data); + + static void + STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len); + + void + PushProcessInputReader (); + + void + PopProcessInputReader (); + + void + ResetProcessInputReader (); + + static size_t + ProcessInputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + Error + HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp); + +private: + //------------------------------------------------------------------ + // For Process only + //------------------------------------------------------------------ + void ControlPrivateStateThread (uint32_t signal); + + DISALLOW_COPY_AND_ASSIGN (Process); + +}; + +} // namespace lldb_private + +#endif // liblldb_Process_h_ diff --git a/include/lldb/Target/RegisterContext.h b/include/lldb/Target/RegisterContext.h new file mode 100644 index 00000000000..dd0e73fc7eb --- /dev/null +++ b/include/lldb/Target/RegisterContext.h @@ -0,0 +1,213 @@ +//===-- RegisterContext.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RegisterContext_h_ +#define liblldb_RegisterContext_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ExecutionContextScope.h" + +namespace lldb_private { + +class RegisterContext : + public std::enable_shared_from_this, + public ExecutionContextScope +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + RegisterContext (Thread &thread, uint32_t concrete_frame_idx); + + virtual + ~RegisterContext (); + + void + InvalidateIfNeeded (bool force); + + //------------------------------------------------------------------ + // Subclasses must override these functions + //------------------------------------------------------------------ + virtual void + InvalidateAllRegisters () = 0; + + virtual size_t + GetRegisterCount () = 0; + + virtual const RegisterInfo * + GetRegisterInfoAtIndex (size_t reg) = 0; + + virtual size_t + GetRegisterSetCount () = 0; + + virtual const RegisterSet * + GetRegisterSet (size_t reg_set) = 0; + + virtual bool + ReadRegister (const RegisterInfo *reg_info, RegisterValue ®_value) = 0; + + virtual bool + WriteRegister (const RegisterInfo *reg_info, const RegisterValue ®_value) = 0; + + // 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. + + virtual bool + ReadAllRegisterValues (lldb::DataBufferSP &data_sp) = 0; + + virtual bool + WriteAllRegisterValues (const lldb::DataBufferSP &data_sp) = 0; + + bool + CopyFromRegisterContext (lldb::RegisterContextSP context); + + virtual uint32_t + ConvertRegisterKindToRegisterNumber (uint32_t kind, uint32_t num) = 0; + + //------------------------------------------------------------------ + // Subclasses can override these functions if desired + //------------------------------------------------------------------ + virtual uint32_t + NumSupportedHardwareBreakpoints (); + + virtual uint32_t + SetHardwareBreakpoint (lldb::addr_t addr, size_t size); + + virtual bool + ClearHardwareBreakpoint (uint32_t hw_idx); + + virtual uint32_t + NumSupportedHardwareWatchpoints (); + + virtual uint32_t + SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write); + + virtual bool + ClearHardwareWatchpoint (uint32_t hw_index); + + virtual bool + HardwareSingleStep (bool enable); + + virtual Error + ReadRegisterValueFromMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t src_addr, uint32_t src_len, RegisterValue ®_value); + + virtual Error + WriteRegisterValueToMemory (const lldb_private::RegisterInfo *reg_info, lldb::addr_t dst_addr, uint32_t dst_len, const RegisterValue ®_value); + + //------------------------------------------------------------------ + // Subclasses should not override these + //------------------------------------------------------------------ + virtual lldb::tid_t + GetThreadID() const; + + virtual Thread & + GetThread () + { + return m_thread; + } + + const RegisterInfo * + GetRegisterInfoByName (const char *reg_name, uint32_t start_idx = 0); + + uint64_t + GetPC (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + bool + SetPC (uint64_t pc); + + uint64_t + GetSP (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + bool + SetSP (uint64_t sp); + + uint64_t + GetFP (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + bool + SetFP (uint64_t fp); + + const char * + GetRegisterName (uint32_t reg); + + uint64_t + GetReturnAddress (uint64_t fail_value = LLDB_INVALID_ADDRESS); + + uint64_t + GetFlags (uint64_t fail_value = 0); + + uint64_t + ReadRegisterAsUnsigned (uint32_t reg, uint64_t fail_value); + + uint64_t + ReadRegisterAsUnsigned (const RegisterInfo *reg_info, uint64_t fail_value); + + bool + WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval); + + bool + WriteRegisterFromUnsigned (const RegisterInfo *reg_info, uint64_t uval); + bool + ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t& target_regnum); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + uint32_t + GetStopID () const + { + return m_stop_id; + } + + void + SetStopID (uint32_t stop_id) + { + m_stop_id = stop_id; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from RegisterContext can see and modify these + //------------------------------------------------------------------ + Thread &m_thread; // The thread that this register context belongs to. + uint32_t m_concrete_frame_idx; // The concrete frame index for this register context + uint32_t m_stop_id; // The stop ID that any data in this context is valid for +private: + //------------------------------------------------------------------ + // For RegisterContext only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (RegisterContext); +}; + +} // namespace lldb_private + +#endif // liblldb_RegisterContext_h_ diff --git a/include/lldb/Target/SectionLoadList.h b/include/lldb/Target/SectionLoadList.h new file mode 100644 index 00000000000..ac05bf7a9cb --- /dev/null +++ b/include/lldb/Target/SectionLoadList.h @@ -0,0 +1,89 @@ +//===-- SectionLoadList.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_SectionLoadList_h_ +#define liblldb_SectionLoadList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "llvm/ADT/DenseMap.h" +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class SectionLoadList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + SectionLoadList () : + m_addr_to_sect (), + m_sect_to_addr (), + m_mutex (Mutex::eMutexTypeRecursive) + + { + } + + ~SectionLoadList() + { + // Call clear since this takes a lock and clears the section load list + // in case another thread is currently using this section load list + Clear(); + } + + bool + IsEmpty() const; + + void + Clear (); + + lldb::addr_t + GetSectionLoadAddress (const lldb::SectionSP §ion_sp) const; + + bool + ResolveLoadAddress (lldb::addr_t load_addr, Address &so_addr) const; + + bool + SetSectionLoadAddress (const lldb::SectionSP §ion_sp, lldb::addr_t load_addr, bool warn_multiple = false); + + // The old load address should be specified when unloading to ensure we get + // the correct instance of the section as a shared library could be loaded + // at more than one location. + bool + SetSectionUnloaded (const lldb::SectionSP §ion_sp, lldb::addr_t load_addr); + + // Unload all instances of a section. This function can be used on systems + // that don't support multiple copies of the same shared library to be + // loaded at the same time. + size_t + SetSectionUnloaded (const lldb::SectionSP §ion_sp); + + void + Dump (Stream &s, Target *target); + +protected: + typedef std::map addr_to_sect_collection; + typedef llvm::DenseMap sect_to_addr_collection; + addr_to_sect_collection m_addr_to_sect; + sect_to_addr_collection m_sect_to_addr; + mutable Mutex m_mutex; + +private: + DISALLOW_COPY_AND_ASSIGN (SectionLoadList); +}; + +} // namespace lldb_private + +#endif // liblldb_SectionLoadList_h_ diff --git a/include/lldb/Target/StackFrame.h b/include/lldb/Target/StackFrame.h new file mode 100644 index 00000000000..877bd8ce661 --- /dev/null +++ b/include/lldb/Target/StackFrame.h @@ -0,0 +1,207 @@ +//===-- StackFrame.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StackFrame_h_ +#define liblldb_StackFrame_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Flags.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackID.h" + +namespace lldb_private { + +class StackFrame : + public std::enable_shared_from_this, + public ExecutionContextScope +{ +public: + enum ExpressionPathOption + { + eExpressionPathOptionCheckPtrVsMember = (1u << 0), + eExpressionPathOptionsNoFragileObjcIvar = (1u << 1), + eExpressionPathOptionsNoSyntheticChildren = (1u << 2), + eExpressionPathOptionsNoSyntheticArrayRange = (1u << 3), + eExpressionPathOptionsAllowDirectIVarAccess = (1u << 4) + }; + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackFrame (const lldb::ThreadSP &thread_sp, + lldb::user_id_t frame_idx, + lldb::user_id_t concrete_frame_idx, + lldb::addr_t cfa, + lldb::addr_t pc, + const SymbolContext *sc_ptr); + + StackFrame (const lldb::ThreadSP &thread_sp, + lldb::user_id_t frame_idx, + lldb::user_id_t concrete_frame_idx, + const lldb::RegisterContextSP ®_context_sp, + lldb::addr_t cfa, + lldb::addr_t pc, + const SymbolContext *sc_ptr); + + StackFrame (const lldb::ThreadSP &thread_sp, + lldb::user_id_t frame_idx, + lldb::user_id_t concrete_frame_idx, + const lldb::RegisterContextSP ®_context_sp, + lldb::addr_t cfa, + const Address& pc, + const SymbolContext *sc_ptr); + + virtual ~StackFrame (); + + lldb::ThreadSP + GetThread () const + { + return m_thread_wp.lock(); + } + + StackID& + GetStackID(); + + const Address& + GetFrameCodeAddress(); + + void + ChangePC (lldb::addr_t pc); + + const SymbolContext& + GetSymbolContext (uint32_t resolve_scope); + + bool + GetFrameBaseValue(Scalar &value, Error *error_ptr); + + Block * + GetFrameBlock (); + + lldb::RegisterContextSP + GetRegisterContext (); + + const lldb::RegisterContextSP & + GetRegisterContextSP () const + { + return m_reg_context_sp; + } + + VariableList * + GetVariableList (bool get_file_globals); + + lldb::VariableListSP + GetInScopeVariableList (bool get_file_globals); + + // See ExpressionPathOption enumeration for "options" values + lldb::ValueObjectSP + GetValueForVariableExpressionPath (const char *var_expr, + lldb::DynamicValueType use_dynamic, + uint32_t options, + lldb::VariableSP &var_sp, + Error &error); + + bool + HasDebugInformation (); + + const char * + Disassemble (); + + void + DumpUsingSettingsFormat (Stream *strm); + + void + Dump (Stream *strm, bool show_frame_index, bool show_fullpaths); + + bool + IsInlined (); + + uint32_t + GetFrameIndex () const; + + uint32_t + GetConcreteFrameIndex () const + { + return m_concrete_frame_index; + } + + lldb::ValueObjectSP + GetValueObjectForFrameVariable (const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic); + + lldb::ValueObjectSP + TrackGlobalVariable (const lldb::VariableSP &variable_sp, lldb::DynamicValueType use_dynamic); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + bool + GetStatus (Stream &strm, + bool show_frame_info, + bool show_source); + +protected: + friend class StackFrameList; + + void + SetSymbolContextScope (SymbolContextScope *symbol_scope); + + void + UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame); + + void + UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame); + + bool + HasCachedData () const; + +private: + //------------------------------------------------------------------ + // For StackFrame only + //------------------------------------------------------------------ + lldb::ThreadWP m_thread_wp; + uint32_t m_frame_index; + uint32_t m_concrete_frame_index; + lldb::RegisterContextSP m_reg_context_sp; + StackID m_id; + Address m_frame_code_addr; // The frame code address (might not be the same as the actual PC for inlined frames) as a section/offset address + SymbolContext m_sc; + Flags m_flags; + Scalar m_frame_base; + Error m_frame_base_error; + lldb::VariableListSP m_variable_list_sp; + ValueObjectList m_variable_list_value_objects; // Value objects for each variable in m_variable_list_sp + StreamString m_disassembly; + DISALLOW_COPY_AND_ASSIGN (StackFrame); +}; + +} // namespace lldb_private + +#endif // liblldb_StackFrame_h_ diff --git a/include/lldb/Target/StackFrameList.h b/include/lldb/Target/StackFrameList.h new file mode 100644 index 00000000000..b2689d0391e --- /dev/null +++ b/include/lldb/Target/StackFrameList.h @@ -0,0 +1,157 @@ +//===-- StackFrameList.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StackFrameList_h_ +#define liblldb_StackFrameList_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Host/Mutex.h" +#include "lldb/Target/StackFrame.h" + +namespace lldb_private { + +class StackFrameList +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackFrameList (Thread &thread, + const lldb::StackFrameListSP &prev_frames_sp, + bool show_inline_frames); + + ~StackFrameList(); + + uint32_t + GetNumFrames (bool can_create = true); + + lldb::StackFrameSP + GetFrameAtIndex (uint32_t idx); + + lldb::StackFrameSP + GetFrameWithConcreteFrameIndex (uint32_t unwind_idx); + + lldb::StackFrameSP + GetFrameWithStackID (const StackID &stack_id); + + // Mark a stack frame as the current frame + uint32_t + SetSelectedFrame (lldb_private::StackFrame *frame); + + uint32_t + GetSelectedFrameIndex () const; + + // Mark a stack frame as the current frame using the frame index + bool + SetSelectedFrameByIndex (uint32_t idx); + + uint32_t + GetVisibleStackFrameIndex(uint32_t idx) + { + if (m_current_inlined_depth < UINT32_MAX) + return idx - m_current_inlined_depth; + else + return idx; + } + + void + CalculateCurrentInlinedDepth (); + + void + SetDefaultFileAndLineToSelectedFrame(); + + void + Clear (); + + void + InvalidateFrames (uint32_t start_idx); + + void + Dump (Stream *s); + + lldb::StackFrameSP + GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr); + + size_t + GetStatus (Stream &strm, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source); + +protected: + + friend class Thread; + + bool + SetFrameAtIndex (uint32_t idx, lldb::StackFrameSP &frame_sp); + + static void + Merge (std::unique_ptr& curr_ap, + lldb::StackFrameListSP& prev_sp); + + void + GetFramesUpTo (uint32_t end_idx); + + bool + GetAllFramesFetched() + { + return m_concrete_frames_fetched == UINT32_MAX; + } + + void + SetAllFramesFetched () + { + m_concrete_frames_fetched = UINT32_MAX; + } + + bool + DecrementCurrentInlinedDepth (); + + void + ResetCurrentInlinedDepth(); + + uint32_t + GetCurrentInlinedDepth (); + + void + SetCurrentInlinedDepth (uint32_t new_depth); + + //------------------------------------------------------------------ + // Classes that inherit from StackFrameList can see and modify these + //------------------------------------------------------------------ + typedef std::vector collection; + typedef collection::iterator iterator; + typedef collection::const_iterator const_iterator; + + Thread &m_thread; + lldb::StackFrameListSP m_prev_frames_sp; + mutable Mutex m_mutex; + collection m_frames; + uint32_t m_selected_frame_idx; + uint32_t m_concrete_frames_fetched; + uint32_t m_current_inlined_depth; + lldb::addr_t m_current_inlined_pc; + bool m_show_inlined_frames; + +private: + //------------------------------------------------------------------ + // For StackFrameList only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (StackFrameList); +}; + +} // namespace lldb_private + +#endif // liblldb_StackFrameList_h_ diff --git a/include/lldb/Target/StackID.h b/include/lldb/Target/StackID.h new file mode 100644 index 00000000000..7e713c73d97 --- /dev/null +++ b/include/lldb/Target/StackID.h @@ -0,0 +1,149 @@ +//===-- StackID.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StackID_h_ +#define liblldb_StackID_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/AddressRange.h" + +namespace lldb_private { + +class StackID +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StackID () : + m_pc (LLDB_INVALID_ADDRESS), + m_cfa (LLDB_INVALID_ADDRESS), + m_symbol_scope (NULL) + { + } + + explicit + StackID (lldb::addr_t pc, lldb::addr_t cfa, SymbolContextScope *symbol_scope) : + m_pc (pc), + m_cfa (cfa), + m_symbol_scope (symbol_scope) + { + } + + StackID (const StackID& rhs) : + m_pc (rhs.m_pc), + m_cfa (rhs.m_cfa), + m_symbol_scope (rhs.m_symbol_scope) + { + } + + ~StackID() + { + } + + lldb::addr_t + GetPC() const + { + return m_pc; + } + + lldb::addr_t + GetCallFrameAddress() const + { + return m_cfa; + } + + SymbolContextScope * + GetSymbolContextScope () const + { + return m_symbol_scope; + } + + void + SetSymbolContextScope (SymbolContextScope *symbol_scope) + { + m_symbol_scope = symbol_scope; + } + + void + Clear () + { + m_pc = LLDB_INVALID_ADDRESS; + m_cfa = LLDB_INVALID_ADDRESS; + m_symbol_scope = NULL; + } + + bool + IsValid () const + { + return m_pc != LLDB_INVALID_ADDRESS || m_cfa != LLDB_INVALID_ADDRESS; + } + + void + Dump (Stream *s); + + //------------------------------------------------------------------ + // Operators + //------------------------------------------------------------------ + const StackID& + operator=(const StackID& rhs) + { + if (this != &rhs) + { + m_pc = rhs.m_pc; + m_cfa = rhs.m_cfa; + m_symbol_scope = rhs.m_symbol_scope; + } + return *this; + } + +protected: + + friend class StackFrame; + + void + SetPC (lldb::addr_t pc) + { + m_pc = pc; + } + + + //------------------------------------------------------------------ + // Classes that inherit from StackID can see and modify these + //------------------------------------------------------------------ + lldb::addr_t m_pc; // The pc value for the function/symbol for this frame. This will + // only get used if the symbol scope is NULL (the code where we are + // stopped is not represented by any function or symbol in any + // shared library). + lldb::addr_t m_cfa; // The call frame address (stack pointer) value + // at the beginning of the function that uniquely + // identifies this frame (along with m_symbol_scope below) + SymbolContextScope *m_symbol_scope; // If NULL, there is no block or symbol for this frame. + // If not NULL, this will either be the scope for the + // lexical block for the frame, or the scope + // for the symbol. Symbol context scopes are + // always be unique pointers since the are part + // of the Block and Symbol objects and can easily + // be used to tell if a stack ID is the same as + // another. +}; + +bool operator== (const StackID& lhs, const StackID& rhs); +bool operator!= (const StackID& lhs, const StackID& rhs); + +// frame_id_1 < frame_id_2 means "frame_id_1 is YOUNGER than frame_id_2" +bool operator< (const StackID& lhs, const StackID& rhs); + +} // namespace lldb_private + +#endif // liblldb_StackID_h_ diff --git a/include/lldb/Target/StopInfo.h b/include/lldb/Target/StopInfo.h new file mode 100644 index 00000000000..3435d392e2b --- /dev/null +++ b/include/lldb/Target/StopInfo.h @@ -0,0 +1,227 @@ +//===-- StopInfo.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_StopInfo_h_ +#define liblldb_StopInfo_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Target/Process.h" + +namespace lldb_private { + +class StopInfo +{ + friend class Process::ProcessEventData; + friend class ThreadPlanBase; + +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + StopInfo (Thread &thread, uint64_t value); + + virtual ~StopInfo() + { + } + + + bool + IsValid () const; + + void + SetThread (const lldb::ThreadSP &thread_sp) + { + m_thread_wp = thread_sp; + } + + lldb::ThreadSP + GetThread() const + { + return m_thread_wp.lock(); + } + + // The value of the StopInfo depends on the StopReason. + // StopReason Meaning + // ---------------------------------------------- + // eStopReasonBreakpoint BreakpointSiteID + // eStopReasonSignal Signal number + // eStopReasonWatchpoint WatchpointLocationID + // eStopReasonPlanComplete No significance + + uint64_t + GetValue() const + { + return m_value; + } + + virtual lldb::StopReason + GetStopReason () const = 0; + + // ShouldStopSynchronous will get called before any thread plans are consulted, and if it says we should + // resume the target, then we will just immediately resume. This should not run any code in or resume the + // target. + + virtual bool + ShouldStopSynchronous (Event *event_ptr) + { + return true; + } + + void + OverrideShouldNotify (bool override_value) + { + m_override_should_notify = override_value ? eLazyBoolYes : eLazyBoolNo; + } + + // If should stop returns false, check if we should notify of this event + virtual bool + ShouldNotify (Event *event_ptr) + { + if (m_override_should_notify == eLazyBoolCalculate) + return DoShouldNotify (event_ptr); + else + return m_override_should_notify == eLazyBoolYes; + } + + virtual void + WillResume (lldb::StateType resume_state) + { + // By default, don't do anything + } + + virtual const char * + GetDescription () + { + return m_description.c_str(); + } + + virtual void + SetDescription (const char *desc_cstr) + { + if (desc_cstr && desc_cstr[0]) + m_description.assign (desc_cstr); + else + m_description.clear(); + } + + // Sometimes the thread plan logic will know that it wants a given stop to stop or not, + // regardless of what the ordinary logic for that StopInfo would dictate. The main example + // of this is the ThreadPlanCallFunction, which for instance knows - based on how that particular + // expression was executed - whether it wants all breakpoints to auto-continue or not. + // Use OverrideShouldStop on the StopInfo to implement this. + + void + OverrideShouldStop (bool override_value) + { + m_override_should_stop = override_value ? eLazyBoolYes : eLazyBoolNo; + } + + bool + GetOverrideShouldStop() + { + return m_override_should_stop != eLazyBoolCalculate; + } + + bool + GetOverriddenShouldStopValue () + { + return m_override_should_stop == eLazyBoolYes; + } + + static lldb::StopInfoSP + CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id); + + // This creates a StopInfo for the thread where the should_stop is already set, and won't be recalculated. + static lldb::StopInfoSP + CreateStopReasonWithBreakpointSiteID (Thread &thread, lldb::break_id_t break_id, bool should_stop); + + static lldb::StopInfoSP + CreateStopReasonWithWatchpointID (Thread &thread, lldb::break_id_t watch_id); + + static lldb::StopInfoSP + CreateStopReasonWithSignal (Thread &thread, int signo); + + static lldb::StopInfoSP + CreateStopReasonToTrace (Thread &thread); + + static lldb::StopInfoSP + CreateStopReasonWithPlan (lldb::ThreadPlanSP &plan, lldb::ValueObjectSP return_valobj_sp); + + static lldb::StopInfoSP + CreateStopReasonWithException (Thread &thread, const char *description); + + static lldb::StopInfoSP + CreateStopReasonWithExec (Thread &thread); + + static lldb::ValueObjectSP + GetReturnValueObject (lldb::StopInfoSP &stop_info_sp); + +protected: + // Perform any action that is associated with this stop. This is done as the + // Event is removed from the event queue. ProcessEventData::DoOnRemoval does the job. + + virtual void + PerformAction (Event *event_ptr) + { + } + + virtual bool + DoShouldNotify (Event *event_ptr) + { + return false; + } + + // Stop the thread by default. Subclasses can override this to allow + // the thread to continue if desired. The ShouldStop method should not do anything + // that might run code. If you need to run code when deciding whether to stop + // at this StopInfo, that must be done in the PerformAction. + // The PerformAction will always get called before the ShouldStop. This is done by the + // ProcessEventData::DoOnRemoval, though the ThreadPlanBase needs to consult this later on. + virtual bool + ShouldStop (Event *event_ptr) + { + return true; + } + + //------------------------------------------------------------------ + // Classes that inherit from StackID can see and modify these + //------------------------------------------------------------------ + lldb::ThreadWP m_thread_wp; // The thread corresponding to the stop reason. + uint32_t m_stop_id; // The process stop ID for which this stop info is valid + uint32_t m_resume_id; // This is the resume ID when we made this stop ID. + uint64_t m_value; // A generic value that can be used for things pertaining to this stop info + std::string m_description; // A textual description describing this stop. + LazyBool m_override_should_notify; + LazyBool m_override_should_stop; + + // This determines whether the target has run since this stop info. + // N.B. running to evaluate a user expression does not count. + bool HasTargetRunSinceMe (); + + // MakeStopInfoValid is necessary to allow saved stop infos to resurrect themselves as valid. + // It should only be used by Thread::RestoreThreadStateFromCheckpoint and to make sure the one-step + // needed for before-the-fact watchpoints does not prevent us from stopping + void + MakeStopInfoValid (); + +private: + friend class Thread; + + DISALLOW_COPY_AND_ASSIGN (StopInfo); +}; + +} // namespace lldb_private + +#endif // liblldb_StopInfo_h_ diff --git a/include/lldb/Target/Target.h b/include/lldb/Target/Target.h new file mode 100644 index 00000000000..87fa57b3a29 --- /dev/null +++ b/include/lldb/Target/Target.h @@ -0,0 +1,1223 @@ +//===-- Target.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Target_h_ +#define liblldb_Target_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/Breakpoint/BreakpointList.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueEnumeration.h" +#include "lldb/Interpreter/OptionValueFileSpec.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ABI.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/PathMappingList.h" +#include "lldb/Target/SectionLoadList.h" + +namespace lldb_private { + +extern OptionEnumValueElement g_dynamic_value_types[]; + +typedef enum InlineStrategy +{ + eInlineBreakpointsNever = 0, + eInlineBreakpointsHeaders, + eInlineBreakpointsAlways +} InlineStrategy; + +typedef enum LoadScriptFromSymFile +{ + eLoadScriptFromSymFileTrue, + eLoadScriptFromSymFileFalse, + eLoadScriptFromSymFileWarn +} LoadScriptFromSymFile; + +//---------------------------------------------------------------------- +// TargetProperties +//---------------------------------------------------------------------- +class TargetProperties : public Properties +{ +public: + TargetProperties(Target *target); + + virtual + ~TargetProperties(); + + ArchSpec + GetDefaultArchitecture () const; + + void + SetDefaultArchitecture (const ArchSpec& arch); + + lldb::DynamicValueType + GetPreferDynamicValue() const; + + bool + GetDisableASLR () const; + + void + SetDisableASLR (bool b); + + bool + GetDisableSTDIO () const; + + void + SetDisableSTDIO (bool b); + + const char * + GetDisassemblyFlavor() const; + +// void +// SetDisassemblyFlavor(const char *flavor); + + InlineStrategy + GetInlineStrategy () const; + + const char * + GetArg0 () const; + + void + SetArg0 (const char *arg); + + bool + GetRunArguments (Args &args) const; + + void + SetRunArguments (const Args &args); + + size_t + GetEnvironmentAsArgs (Args &env) const; + + bool + GetSkipPrologue() const; + + PathMappingList & + GetSourcePathMap () const; + + FileSpecList & + GetExecutableSearchPaths (); + + FileSpecList & + GetDebugFileSearchPaths (); + + bool + GetEnableSyntheticValue () const; + + uint32_t + GetMaximumNumberOfChildrenToDisplay() const; + + uint32_t + GetMaximumSizeOfStringSummary() const; + + uint32_t + GetMaximumMemReadSize () const; + + FileSpec + GetStandardInputPath () const; + + void + SetStandardInputPath (const char *path); + + FileSpec + GetStandardOutputPath () const; + + void + SetStandardOutputPath (const char *path); + + FileSpec + GetStandardErrorPath () const; + + void + SetStandardErrorPath (const char *path); + + bool + GetBreakpointsConsultPlatformAvoidList (); + + const char * + GetExpressionPrefixContentsAsCString (); + + bool + GetUseHexImmediates() const; + + bool + GetUseFastStepping() const; + + LoadScriptFromSymFile + GetLoadScriptFromSymbolFile() const; + + Disassembler::HexImmediateStyle + GetHexImmediateStyle() const; + + MemoryModuleLoadLevel + GetMemoryModuleLoadLevel() const; + +}; + +typedef std::shared_ptr TargetPropertiesSP; + +class EvaluateExpressionOptions +{ +public: + static const uint32_t default_timeout = 500000; + EvaluateExpressionOptions() : + m_execution_policy(eExecutionPolicyOnlyWhenNeeded), + m_coerce_to_id(false), + m_unwind_on_error(true), + m_ignore_breakpoints (false), + m_keep_in_memory(false), + m_run_others(true), + m_use_dynamic(lldb::eNoDynamicValues), + m_timeout_usec(default_timeout) + {} + + ExecutionPolicy + GetExecutionPolicy () const + { + return m_execution_policy; + } + + EvaluateExpressionOptions& + SetExecutionPolicy (ExecutionPolicy policy = eExecutionPolicyAlways) + { + m_execution_policy = policy; + return *this; + } + + bool + DoesCoerceToId () const + { + return m_coerce_to_id; + } + + EvaluateExpressionOptions& + SetCoerceToId (bool coerce = true) + { + m_coerce_to_id = coerce; + return *this; + } + + bool + DoesUnwindOnError () const + { + return m_unwind_on_error; + } + + EvaluateExpressionOptions& + SetUnwindOnError (bool unwind = false) + { + m_unwind_on_error = unwind; + return *this; + } + + bool + DoesIgnoreBreakpoints () const + { + return m_ignore_breakpoints; + } + + EvaluateExpressionOptions& + SetIgnoreBreakpoints (bool ignore = false) + { + m_ignore_breakpoints = ignore; + return *this; + } + + bool + DoesKeepInMemory () const + { + return m_keep_in_memory; + } + + EvaluateExpressionOptions& + SetKeepInMemory (bool keep = true) + { + m_keep_in_memory = keep; + return *this; + } + + lldb::DynamicValueType + GetUseDynamic () const + { + return m_use_dynamic; + } + + EvaluateExpressionOptions& + SetUseDynamic (lldb::DynamicValueType dynamic = lldb::eDynamicCanRunTarget) + { + m_use_dynamic = dynamic; + return *this; + } + + uint32_t + GetTimeoutUsec () const + { + return m_timeout_usec; + } + + EvaluateExpressionOptions& + SetTimeoutUsec (uint32_t timeout = 0) + { + m_timeout_usec = timeout; + return *this; + } + + bool + GetRunOthers () const + { + return m_run_others; + } + + EvaluateExpressionOptions& + SetRunOthers (bool run_others = true) + { + m_run_others = run_others; + return *this; + } + +private: + ExecutionPolicy m_execution_policy; + bool m_coerce_to_id; + bool m_unwind_on_error; + bool m_ignore_breakpoints; + bool m_keep_in_memory; + bool m_run_others; + lldb::DynamicValueType m_use_dynamic; + uint32_t m_timeout_usec; +}; + +//---------------------------------------------------------------------- +// Target +//---------------------------------------------------------------------- +class Target : + public std::enable_shared_from_this, + public TargetProperties, + public Broadcaster, + public ExecutionContextScope, + public ModuleList::Notifier +{ +public: + friend class TargetList; + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitBreakpointChanged = (1 << 0), + eBroadcastBitModulesLoaded = (1 << 1), + eBroadcastBitModulesUnloaded = (1 << 2), + eBroadcastBitWatchpointChanged = (1 << 3), + eBroadcastBitSymbolsLoaded = (1 << 4) + }; + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + // This event data class is for use by the TargetList to broadcast new target notifications. + class TargetEventData : public EventData + { + public: + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const; + + TargetEventData (const lldb::TargetSP &new_target_sp); + + lldb::TargetSP & + GetTarget() + { + return m_target_sp; + } + + virtual + ~TargetEventData(); + + virtual void + Dump (Stream *s) const; + + static const lldb::TargetSP + GetTargetFromEvent (const lldb::EventSP &event_sp); + + static const TargetEventData * + GetEventDataFromEvent (const Event *event_sp); + + private: + lldb::TargetSP m_target_sp; + + DISALLOW_COPY_AND_ASSIGN (TargetEventData); + }; + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + +// static lldb::UserSettingsControllerSP & +// GetSettingsController (); + + static FileSpecList + GetDefaultExecutableSearchPaths (); + + static FileSpecList + GetDefaultDebugFileSearchPaths (); + + static ArchSpec + GetDefaultArchitecture (); + + static void + SetDefaultArchitecture (const ArchSpec &arch); + +// void +// UpdateInstanceName (); + + lldb::ModuleSP + GetSharedModule (const ModuleSpec &module_spec, + Error *error_ptr = NULL); + + //---------------------------------------------------------------------- + // Settings accessors + //---------------------------------------------------------------------- + + static const TargetPropertiesSP & + GetGlobalProperties(); + + +private: + //------------------------------------------------------------------ + /// Construct with optional file and arch. + /// + /// This member is private. Clients must use + /// TargetList::CreateTarget(const FileSpec*, const ArchSpec*) + /// so all targets can be tracked from the central target list. + /// + /// @see TargetList::CreateTarget(const FileSpec*, const ArchSpec*) + //------------------------------------------------------------------ + Target (Debugger &debugger, + const ArchSpec &target_arch, + const lldb::PlatformSP &platform_sp); + + // Helper function. + bool + ProcessIsValid (); + +public: + ~Target(); + + Mutex & + GetAPIMutex () + { + return m_mutex; + } + + void + DeleteCurrentProcess (); + + void + CleanupProcess (); + //------------------------------------------------------------------ + /// Dump a description of this object to a Stream. + /// + /// Dump a description of the contents of this object to the + /// supplied stream \a s. The dumped content will be only what has + /// been loaded or parsed up to this point at which this function + /// is called, so this is a good way to see what has been parsed + /// in a target. + /// + /// @param[in] s + /// The stream to which to dump the object descripton. + //------------------------------------------------------------------ + void + Dump (Stream *s, lldb::DescriptionLevel description_level); + + const lldb::ProcessSP & + CreateProcess (Listener &listener, + const char *plugin_name, + const FileSpec *crash_file); + + const lldb::ProcessSP & + GetProcessSP () const; + + bool + IsValid() + { + return m_valid; + } + + void + Destroy(); + + //------------------------------------------------------------------ + // This part handles the breakpoints. + //------------------------------------------------------------------ + + BreakpointList & + GetBreakpointList(bool internal = false); + + const BreakpointList & + GetBreakpointList(bool internal = false) const; + + lldb::BreakpointSP + GetLastCreatedBreakpoint () + { + return m_last_created_breakpoint; + } + + lldb::BreakpointSP + GetBreakpointByID (lldb::break_id_t break_id); + + // Use this to create a file and line breakpoint to a given module or all module it is NULL + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpec &file, + uint32_t line_no, + LazyBool check_inlines = eLazyBoolCalculate, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + // Use this to create breakpoint that matches regex against the source lines in files given in source_file_list: + lldb::BreakpointSP + CreateSourceRegexBreakpoint (const FileSpecList *containingModules, + const FileSpecList *source_file_list, + RegularExpression &source_regex, + bool internal = false); + + // Use this to create a breakpoint from a load address + lldb::BreakpointSP + CreateBreakpoint (lldb::addr_t load_addr, + bool internal = false); + + // Use this to create Address breakpoints: + lldb::BreakpointSP + CreateBreakpoint (Address &addr, + bool internal = false); + + // Use this to create a function breakpoint by regexp in containingModule/containingSourceFiles, or all modules if it is NULL + // When "skip_prologue is set to eLazyBoolCalculate, we use the current target + // setting, else we use the values passed in + lldb::BreakpointSP + CreateFuncRegexBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + RegularExpression &func_regexp, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + // Use this to create a function breakpoint by name in containingModule, or all modules if it is NULL + // When "skip_prologue is set to eLazyBoolCalculate, we use the current target + // setting, else we use the values passed in + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const char *func_name, + uint32_t func_name_type_mask, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + lldb::BreakpointSP + CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal = false); + + // This is the same as the func_name breakpoint except that you can specify a vector of names. This is cheaper + // than a regular expression breakpoint in the case where you just want to set a breakpoint on a set of names + // you already know. + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const char *func_names[], + size_t num_names, + uint32_t func_name_type_mask, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + lldb::BreakpointSP + CreateBreakpoint (const FileSpecList *containingModules, + const FileSpecList *containingSourceFiles, + const std::vector &func_names, + uint32_t func_name_type_mask, + LazyBool skip_prologue = eLazyBoolCalculate, + bool internal = false); + + + // Use this to create a general breakpoint: + lldb::BreakpointSP + CreateBreakpoint (lldb::SearchFilterSP &filter_sp, + lldb::BreakpointResolverSP &resolver_sp, + bool internal = false); + + // Use this to create a watchpoint: + lldb::WatchpointSP + CreateWatchpoint (lldb::addr_t addr, + size_t size, + const ClangASTType *type, + uint32_t kind, + Error &error); + + lldb::WatchpointSP + GetLastCreatedWatchpoint () + { + return m_last_created_watchpoint; + } + + WatchpointList & + GetWatchpointList() + { + return m_watchpoint_list; + } + + void + RemoveAllBreakpoints (bool internal_also = false); + + void + DisableAllBreakpoints (bool internal_also = false); + + void + EnableAllBreakpoints (bool internal_also = false); + + bool + DisableBreakpointByID (lldb::break_id_t break_id); + + bool + EnableBreakpointByID (lldb::break_id_t break_id); + + bool + RemoveBreakpointByID (lldb::break_id_t break_id); + + // The flag 'end_to_end', default to true, signifies that the operation is + // performed end to end, for both the debugger and the debuggee. + + bool + RemoveAllWatchpoints (bool end_to_end = true); + + bool + DisableAllWatchpoints (bool end_to_end = true); + + bool + EnableAllWatchpoints (bool end_to_end = true); + + bool + ClearAllWatchpointHitCounts (); + + bool + IgnoreAllWatchpoints (uint32_t ignore_count); + + bool + DisableWatchpointByID (lldb::watch_id_t watch_id); + + bool + EnableWatchpointByID (lldb::watch_id_t watch_id); + + bool + RemoveWatchpointByID (lldb::watch_id_t watch_id); + + bool + IgnoreWatchpointByID (lldb::watch_id_t watch_id, uint32_t ignore_count); + + //------------------------------------------------------------------ + /// Get \a load_addr as a callable code load address for this target + /// + /// Take \a load_addr and potentially add any address bits that are + /// needed to make the address callable. For ARM this can set bit + /// zero (if it already isn't) if \a load_addr is a thumb function. + /// If \a addr_class is set to eAddressClassInvalid, then the address + /// adjustment will always happen. If it is set to an address class + /// that doesn't have code in it, LLDB_INVALID_ADDRESS will be + /// returned. + //------------------------------------------------------------------ + lldb::addr_t + GetCallableLoadAddress (lldb::addr_t load_addr, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const; + + //------------------------------------------------------------------ + /// Get \a load_addr as an opcode for this target. + /// + /// Take \a load_addr and potentially strip any address bits that are + /// needed to make the address point to an opcode. For ARM this can + /// clear bit zero (if it already isn't) if \a load_addr is a + /// thumb function and load_addr is in code. + /// If \a addr_class is set to eAddressClassInvalid, then the address + /// adjustment will always happen. If it is set to an address class + /// that doesn't have code in it, LLDB_INVALID_ADDRESS will be + /// returned. + //------------------------------------------------------------------ + lldb::addr_t + GetOpcodeLoadAddress (lldb::addr_t load_addr, lldb::AddressClass addr_class = lldb::eAddressClassInvalid) const; + +protected: + //------------------------------------------------------------------ + /// Implementing of ModuleList::Notifier. + //------------------------------------------------------------------ + + virtual void + ModuleAdded (const ModuleList& module_list, const lldb::ModuleSP& module_sp); + + virtual void + ModuleRemoved (const ModuleList& module_list, const lldb::ModuleSP& module_sp); + + virtual void + ModuleUpdated (const ModuleList& module_list, + const lldb::ModuleSP& old_module_sp, + const lldb::ModuleSP& new_module_sp); + virtual void + WillClearList (const ModuleList& module_list); + +public: + + void + ModulesDidLoad (ModuleList &module_list); + + void + ModulesDidUnload (ModuleList &module_list); + + void + SymbolsDidLoad (ModuleList &module_list); + + //------------------------------------------------------------------ + /// Gets the module for the main executable. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. + /// + /// @return + /// The shared pointer to the executable module which can + /// contains a NULL Module object if no executable has been + /// set. + /// + /// @see DynamicLoader + /// @see ObjectFile::GetDependentModules (FileSpecList&) + /// @see Process::SetExecutableModule(lldb::ModuleSP&) + //------------------------------------------------------------------ + lldb::ModuleSP + GetExecutableModule (); + + Module* + GetExecutableModulePointer (); + + //------------------------------------------------------------------ + /// Set the main executable module. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. + /// + /// Setting the executable causes any of the current dependant + /// image information to be cleared and replaced with the static + /// dependent image information found by calling + /// ObjectFile::GetDependentModules (FileSpecList&) on the main + /// executable and any modules on which it depends. Calling + /// Process::GetImages() will return the newly found images that + /// were obtained from all of the object files. + /// + /// @param[in] module_sp + /// A shared pointer reference to the module that will become + /// the main executable for this process. + /// + /// @param[in] get_dependent_files + /// If \b true then ask the object files to track down any + /// known dependent files. + /// + /// @see ObjectFile::GetDependentModules (FileSpecList&) + /// @see Process::GetImages() + //------------------------------------------------------------------ + void + SetExecutableModule (lldb::ModuleSP& module_sp, bool get_dependent_files); + + bool + LoadScriptingResources (std::list& errors, + Stream* feedback_stream = NULL, + bool continue_on_error = true) + { + return m_images.LoadScriptingResourcesInTarget(this,errors,feedback_stream,continue_on_error); + } + + //------------------------------------------------------------------ + /// Get accessor for the images for this process. + /// + /// Each process has a notion of a main executable that is the file + /// that will be executed or attached to. Executable files can have + /// dependent modules that are discovered from the object files, or + /// discovered at runtime as things are dynamically loaded. After + /// a main executable has been set, the images will contain a list + /// of all the files that the executable depends upon as far as the + /// object files know. These images will usually contain valid file + /// virtual addresses only. When the process is launched or attached + /// to, the DynamicLoader plug-in will discover where these images + /// were loaded in memory and will resolve the load virtual + /// addresses is each image, and also in images that are loaded by + /// code. + /// + /// @return + /// A list of Module objects in a module list. + //------------------------------------------------------------------ + const ModuleList& + GetImages () const + { + return m_images; + } + + ModuleList& + GetImages () + { + return m_images; + } + + //------------------------------------------------------------------ + /// Return whether this FileSpec corresponds to a module that should be considered for general searches. + /// + /// This API will be consulted by the SearchFilterForNonModuleSpecificSearches + /// and any module that returns \b true will not be searched. Note the + /// SearchFilterForNonModuleSpecificSearches is the search filter that + /// gets used in the CreateBreakpoint calls when no modules is provided. + /// + /// The target call at present just consults the Platform's call of the + /// same name. + /// + /// @param[in] module_sp + /// A shared pointer reference to the module that checked. + /// + /// @return \b true if the module should be excluded, \b false otherwise. + //------------------------------------------------------------------ + bool + ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_spec); + + //------------------------------------------------------------------ + /// Return whether this module should be considered for general searches. + /// + /// This API will be consulted by the SearchFilterForNonModuleSpecificSearches + /// and any module that returns \b true will not be searched. Note the + /// SearchFilterForNonModuleSpecificSearches is the search filter that + /// gets used in the CreateBreakpoint calls when no modules is provided. + /// + /// The target call at present just consults the Platform's call of the + /// same name. + /// + /// FIXME: When we get time we should add a way for the user to set modules that they + /// don't want searched, in addition to or instead of the platform ones. + /// + /// @param[in] module_sp + /// A shared pointer reference to the module that checked. + /// + /// @return \b true if the module should be excluded, \b false otherwise. + //------------------------------------------------------------------ + bool + ModuleIsExcludedForNonModuleSpecificSearches (const lldb::ModuleSP &module_sp); + + ArchSpec & + GetArchitecture () + { + return m_arch; + } + + const ArchSpec & + GetArchitecture () const + { + return m_arch; + } + + //------------------------------------------------------------------ + /// Set the architecture for this target. + /// + /// If the current target has no Images read in, then this just sets the architecture, which will + /// be used to select the architecture of the ExecutableModule when that is set. + /// If the current target has an ExecutableModule, then calling SetArchitecture with a different + /// architecture from the currently selected one will reset the ExecutableModule to that slice + /// of the file backing the ExecutableModule. If the file backing the ExecutableModule does not + /// contain a fork of this architecture, then this code will return false, and the architecture + /// won't be changed. + /// If the input arch_spec is the same as the already set architecture, this is a no-op. + /// + /// @param[in] arch_spec + /// The new architecture. + /// + /// @return + /// \b true if the architecture was successfully set, \bfalse otherwise. + //------------------------------------------------------------------ + bool + SetArchitecture (const ArchSpec &arch_spec); + + Debugger & + GetDebugger () + { + return m_debugger; + } + + size_t + ReadMemoryFromFileCache (const Address& addr, + void *dst, + size_t dst_len, + Error &error); + + // Reading memory through the target allows us to skip going to the process + // for reading memory if possible and it allows us to try and read from + // any constant sections in our object files on disk. If you always want + // live program memory, read straight from the process. If you possibly + // want to read from const sections in object files, read from the target. + // This version of ReadMemory will try and read memory from the process + // if the process is alive. The order is: + // 1 - if (prefer_file_cache == true) then read from object file cache + // 2 - if there is a valid process, try and read from its memory + // 3 - if (prefer_file_cache == false) then read from object file cache + size_t + ReadMemory (const Address& addr, + bool prefer_file_cache, + void *dst, + size_t dst_len, + Error &error, + lldb::addr_t *load_addr_ptr = NULL); + + size_t + ReadCStringFromMemory (const Address& addr, std::string &out_str, Error &error); + + size_t + ReadCStringFromMemory (const Address& addr, char *dst, size_t dst_max_len, Error &result_error); + + size_t + ReadScalarIntegerFromMemory (const Address& addr, + bool prefer_file_cache, + uint32_t byte_size, + bool is_signed, + Scalar &scalar, + Error &error); + + uint64_t + ReadUnsignedIntegerFromMemory (const Address& addr, + bool prefer_file_cache, + size_t integer_byte_size, + uint64_t fail_value, + Error &error); + + bool + ReadPointerFromMemory (const Address& addr, + bool prefer_file_cache, + Error &error, + Address &pointer_addr); + + SectionLoadList& + GetSectionLoadList() + { + return m_section_load_list; + } + + const SectionLoadList& + GetSectionLoadList() const + { + return m_section_load_list; + } + + static Target * + GetTargetFromContexts (const ExecutionContext *exe_ctx_ptr, + const SymbolContext *sc_ptr); + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + PathMappingList & + GetImageSearchPathList (); + + ClangASTContext * + GetScratchClangASTContext(bool create_on_demand=true); + + ClangASTImporter * + GetClangASTImporter(); + + + // Since expressions results can persist beyond the lifetime of a process, + // and the const expression results are available after a process is gone, + // we provide a way for expressions to be evaluated from the Target itself. + // If an expression is going to be run, then it should have a frame filled + // in in th execution context. + ExecutionResults + EvaluateExpression (const char *expression, + StackFrame *frame, + lldb::ValueObjectSP &result_valobj_sp, + const EvaluateExpressionOptions& options = EvaluateExpressionOptions()); + + ClangPersistentVariables & + GetPersistentVariables() + { + return m_persistent_variables; + } + + //------------------------------------------------------------------ + // Target Stop Hooks + //------------------------------------------------------------------ + class StopHook : public UserID + { + public: + ~StopHook (); + + StopHook (const StopHook &rhs); + + StringList * + GetCommandPointer () + { + return &m_commands; + } + + const StringList & + GetCommands() + { + return m_commands; + } + + lldb::TargetSP & + GetTarget() + { + return m_target_sp; + } + + void + SetCommands (StringList &in_commands) + { + m_commands = in_commands; + } + + // Set the specifier. The stop hook will own the specifier, and is responsible for deleting it when we're done. + void + SetSpecifier (SymbolContextSpecifier *specifier) + { + m_specifier_sp.reset (specifier); + } + + SymbolContextSpecifier * + GetSpecifier () + { + return m_specifier_sp.get(); + } + + // Set the Thread Specifier. The stop hook will own the thread specifier, and is responsible for deleting it when we're done. + void + SetThreadSpecifier (ThreadSpec *specifier); + + ThreadSpec * + GetThreadSpecifier() + { + return m_thread_spec_ap.get(); + } + + bool + IsActive() + { + return m_active; + } + + void + SetIsActive (bool is_active) + { + m_active = is_active; + } + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + + private: + lldb::TargetSP m_target_sp; + StringList m_commands; + lldb::SymbolContextSpecifierSP m_specifier_sp; + std::unique_ptr m_thread_spec_ap; + bool m_active; + + // Use AddStopHook to make a new empty stop hook. The GetCommandPointer and fill it with commands, + // and SetSpecifier to set the specifier shared pointer (can be null, that will match anything.) + StopHook (lldb::TargetSP target_sp, lldb::user_id_t uid); + friend class Target; + }; + typedef std::shared_ptr StopHookSP; + + // Add an empty stop hook to the Target's stop hook list, and returns a shared pointer to it in new_hook. + // Returns the id of the new hook. + lldb::user_id_t + AddStopHook (StopHookSP &new_hook); + + void + RunStopHooks (); + + size_t + GetStopHookSize(); + + bool + SetSuppresStopHooks (bool suppress) + { + bool old_value = m_suppress_stop_hooks; + m_suppress_stop_hooks = suppress; + return old_value; + } + + bool + GetSuppressStopHooks () + { + return m_suppress_stop_hooks; + } + + bool + SetSuppressSyntheticValue (bool suppress) + { + bool old_value = m_suppress_synthetic_value; + m_suppress_synthetic_value = suppress; + return old_value; + } + + bool + GetSuppressSyntheticValue () + { + return m_suppress_synthetic_value; + } + +// StopHookSP & +// GetStopHookByIndex (size_t index); +// + bool + RemoveStopHookByID (lldb::user_id_t uid); + + void + RemoveAllStopHooks (); + + StopHookSP + GetStopHookByID (lldb::user_id_t uid); + + bool + SetStopHookActiveStateByID (lldb::user_id_t uid, bool active_state); + + void + SetAllStopHooksActiveState (bool active_state); + + size_t GetNumStopHooks () const + { + return m_stop_hooks.size(); + } + + StopHookSP + GetStopHookAtIndex (size_t index) + { + if (index >= GetNumStopHooks()) + return StopHookSP(); + StopHookCollection::iterator pos = m_stop_hooks.begin(); + + while (index > 0) + { + pos++; + index--; + } + return (*pos).second; + } + + lldb::PlatformSP + GetPlatform () + { + return m_platform_sp; + } + + void + SetPlatform (const lldb::PlatformSP &platform_sp) + { + m_platform_sp = platform_sp; + } + + SourceManager & + GetSourceManager (); + + //------------------------------------------------------------------ + // Methods. + //------------------------------------------------------------------ + lldb::SearchFilterSP + GetSearchFilterForModule (const FileSpec *containingModule); + + lldb::SearchFilterSP + GetSearchFilterForModuleList (const FileSpecList *containingModuleList); + + lldb::SearchFilterSP + GetSearchFilterForModuleAndCUList (const FileSpecList *containingModules, const FileSpecList *containingSourceFiles); + +protected: + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + Debugger & m_debugger; + lldb::PlatformSP m_platform_sp; ///< The platform for this target. + Mutex m_mutex; ///< An API mutex that is used by the lldb::SB* classes make the SB interface thread safe + ArchSpec m_arch; + ModuleList m_images; ///< The list of images for this process (shared libraries and anything dynamically loaded). + SectionLoadList m_section_load_list; + BreakpointList m_breakpoint_list; + BreakpointList m_internal_breakpoint_list; + lldb::BreakpointSP m_last_created_breakpoint; + WatchpointList m_watchpoint_list; + lldb::WatchpointSP m_last_created_watchpoint; + // We want to tightly control the process destruction process so + // we can correctly tear down everything that we need to, so the only + // class that knows about the process lifespan is this target class. + lldb::ProcessSP m_process_sp; + bool m_valid; + lldb::SearchFilterSP m_search_filter_sp; + PathMappingList m_image_search_paths; + std::unique_ptr m_scratch_ast_context_ap; + std::unique_ptr m_scratch_ast_source_ap; + std::unique_ptr m_ast_importer_ap; + ClangPersistentVariables m_persistent_variables; ///< These are the persistent variables associated with this process for the expression parser. + + std::unique_ptr m_source_manager_ap; + + typedef std::map StopHookCollection; + StopHookCollection m_stop_hooks; + lldb::user_id_t m_stop_hook_next_id; + bool m_suppress_stop_hooks; + bool m_suppress_synthetic_value; + + static void + ImageSearchPathsChanged (const PathMappingList &path_list, + void *baton); + +private: + DISALLOW_COPY_AND_ASSIGN (Target); +}; + +} // namespace lldb_private + +#endif // liblldb_Target_h_ diff --git a/include/lldb/Target/TargetList.h b/include/lldb/Target/TargetList.h new file mode 100644 index 00000000000..41404e11c7f --- /dev/null +++ b/include/lldb/Target/TargetList.h @@ -0,0 +1,239 @@ +//===-- TargetList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_TargetList_h_ +#define liblldb_TargetList_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Broadcaster.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Target.h" + +namespace lldb_private { + +class TargetList : public Broadcaster +{ +private: + friend class Debugger; + + //------------------------------------------------------------------ + /// Constructor + /// + /// The constructor for the target list is private. Clients can + /// get ahold of of the one and only target list through the + /// lldb_private::Debugger::GetSharedInstance().GetTargetList(). + /// + /// @see static TargetList& lldb_private::Debugger::GetTargetList(). + //------------------------------------------------------------------ + TargetList(Debugger &debugger); + +public: + + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitInterrupt = (1 << 0) + }; + + + // These two functions fill out the Broadcaster interface: + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + virtual ~TargetList(); + + //------------------------------------------------------------------ + /// Create a new Target. + /// + /// Clients must use this function to create a Target. This allows + /// a global list of targets to be maintained in a central location + /// so signal handlers and other global functions can use it to + /// locate an appropriate target to deliver asynchronous information + /// to. + /// + /// @param[in] debugger + /// The debugger to associate this target with + /// + /// @param[in] file_spec + /// The main executable file for a debug target. This value + /// can be NULL and the file can be set later using: + /// Target::SetExecutableModule (ModuleSP&) + /// + /// @param[in] triple_cstr + /// A target triple string to be used for the target. This can + /// be NULL if the triple is not known or when attaching to a + /// process. + /// + /// @param[in] get_dependent_modules + /// Track down the dependent modules for an executable and + /// load those into the module list. + /// + /// @param[in] platform_options + /// A pointer to the platform options to use when creating this + /// target. If this value is NULL, then the currently selected + /// platform will be used. + /// + /// @param[out] target_sp + /// A shared pointer to a target that will be filled in if + /// this call is successful. + /// + /// @return + /// An error object that indicates success or failure + //------------------------------------------------------------------ + Error + CreateTarget (Debugger &debugger, + const char *user_exe_path, + const char *triple_cstr, + bool get_dependent_modules, + const OptionGroupPlatform *platform_options, + lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Create a new Target. + /// + /// Same as the function above, but used when you already know the + /// platform you will be using + //------------------------------------------------------------------ + Error + CreateTarget (Debugger &debugger, + const char *user_exe_path, + const ArchSpec& arch, + bool get_dependent_modules, + lldb::PlatformSP &platform_sp, + lldb::TargetSP &target_sp); + + //------------------------------------------------------------------ + /// Delete a Target object from the list. + /// + /// When clients are done with the Target objets, this function + /// should be called to release the memory associated with a target + /// object. + /// + /// @param[in] target_sp + /// The shared pointer to a target. + /// + /// @return + /// Returns \b true if the target was successfully removed from + /// from this target list, \b false otherwise. The client will + /// be left with the last remaining shared pointer to the target + /// in \a target_sp which can then be properly released. + //------------------------------------------------------------------ + bool + DeleteTarget (lldb::TargetSP &target_sp); + + int + GetNumTargets () const; + + lldb::TargetSP + GetTargetAtIndex (uint32_t index) const; + + uint32_t + GetIndexOfTarget (lldb::TargetSP target_sp) const; + + //------------------------------------------------------------------ + /// Find the target that contains has an executable whose path + /// matches \a exe_file_spec, and whose architecture matches + /// \a arch_ptr if arch_ptr is not NULL. + /// + /// @param[in] exe_file_spec + /// A file spec containing a basename, or a full path (directory + /// and basename). If \a exe_file_spec contains only a filename + /// (empty GetDirectory() value) then matching will be done + /// solely based on the filenames and directories won't be + /// compared. If \a exe_file_spec contains a filename and a + /// directory, then both must match. + /// + /// @param[in] exe_arch_ptr + /// If not NULL then the architecture also needs to match, else + /// the architectures will be compared. + /// + /// @return + /// A shared pointer to a target object. The returned shared + /// pointer will contain NULL if no target objects have a + /// executable whose full or partial path matches + /// with a matching process ID. + //------------------------------------------------------------------ + lldb::TargetSP + FindTargetWithExecutableAndArchitecture (const FileSpec &exe_file_spec, + const ArchSpec *exe_arch_ptr = NULL) const; + + //------------------------------------------------------------------ + /// Find the target that contains a process with process ID \a + /// pid. + /// + /// @param[in] pid + /// The process ID to search our target list for. + /// + /// @return + /// A shared pointer to a target object. The returned shared + /// pointer will contain NULL if no target objects own a process + /// with a matching process ID. + //------------------------------------------------------------------ + lldb::TargetSP + FindTargetWithProcessID (lldb::pid_t pid) const; + + lldb::TargetSP + FindTargetWithProcess (lldb_private::Process *process) const; + + lldb::TargetSP + GetTargetSP (Target *target) const; + + //------------------------------------------------------------------ + /// Send an async interrupt to one or all processes. + /// + /// Find the target that contains the process with process ID \a + /// pid and send a LLDB_EVENT_ASYNC_INTERRUPT event to the process's + /// event queue. + /// + /// @param[in] pid + /// The process ID to search our target list for, if \a pid is + /// LLDB_INVALID_PROCESS_ID, then the interrupt will be sent to + /// all processes. + /// + /// @return + /// The number of async interrupts sent. + //------------------------------------------------------------------ + uint32_t + SendAsyncInterrupt (lldb::pid_t pid = LLDB_INVALID_PROCESS_ID); + + uint32_t + SignalIfRunning (lldb::pid_t pid, int signo); + + uint32_t + SetSelectedTarget (Target *target); + + lldb::TargetSP + GetSelectedTarget (); + + +protected: + typedef std::vector collection; + //------------------------------------------------------------------ + // Member variables. + //------------------------------------------------------------------ + collection m_target_list; + mutable Mutex m_target_list_mutex; + uint32_t m_selected_target_idx; +private: + DISALLOW_COPY_AND_ASSIGN (TargetList); +}; + +} // namespace lldb_private + +#endif // liblldb_TargetList_h_ diff --git a/include/lldb/Target/Thread.h b/include/lldb/Target/Thread.h new file mode 100644 index 00000000000..e4e532e4b33 --- /dev/null +++ b/include/lldb/Target/Thread.h @@ -0,0 +1,1067 @@ +//===-- Thread.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Thread_h_ +#define liblldb_Thread_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/UserID.h" +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/StackFrameList.h" + +#define LLDB_THREAD_MAX_STOP_EXC_DATA 8 + +namespace lldb_private { + +class ThreadProperties : public Properties +{ +public: + ThreadProperties(bool is_global); + + virtual + ~ThreadProperties(); + + //------------------------------------------------------------------ + /// The regular expression returned determines symbols that this + /// thread won't stop in during "step-in" operations. + /// + /// @return + /// A pointer to a regular expression to compare against symbols, + /// or NULL if all symbols are allowed. + /// + //------------------------------------------------------------------ + const RegularExpression * + GetSymbolsToAvoidRegexp(); + + bool + GetTraceEnabledState() const; +}; + +typedef std::shared_ptr ThreadPropertiesSP; + +class Thread : + public std::enable_shared_from_this, + public ThreadProperties, + public UserID, + public ExecutionContextScope, + public Broadcaster +{ +public: + //------------------------------------------------------------------ + /// Broadcaster event bits definitions. + //------------------------------------------------------------------ + enum + { + eBroadcastBitStackChanged = (1 << 0), + eBroadcastBitThreadSuspended = (1 << 1), + eBroadcastBitThreadResumed = (1 << 2), + eBroadcastBitSelectedFrameChanged = (1 << 3), + eBroadcastBitThreadSelected = (1 << 4) + }; + + static ConstString &GetStaticBroadcasterClass (); + + virtual ConstString &GetBroadcasterClass() const + { + return GetStaticBroadcasterClass(); + } + + class ThreadEventData : + public EventData + { + public: + ThreadEventData (const lldb::ThreadSP thread_sp); + + ThreadEventData (const lldb::ThreadSP thread_sp, const StackID &stack_id); + + ThreadEventData(); + + virtual ~ThreadEventData(); + + static const ConstString & + GetFlavorString (); + + virtual const ConstString & + GetFlavor () const + { + return ThreadEventData::GetFlavorString (); + } + + virtual void + Dump (Stream *s) const; + + static const ThreadEventData * + GetEventDataFromEvent (const Event *event_ptr); + + static lldb::ThreadSP + GetThreadFromEvent (const Event *event_ptr); + + static StackID + GetStackIDFromEvent (const Event *event_ptr); + + static lldb::StackFrameSP + GetStackFrameFromEvent (const Event *event_ptr); + + lldb::ThreadSP + GetThread () const + { + return m_thread_sp; + } + + StackID + GetStackID () const + { + return m_stack_id; + } + + private: + lldb::ThreadSP m_thread_sp; + StackID m_stack_id; + DISALLOW_COPY_AND_ASSIGN (ThreadEventData); + }; + + // TODO: You shouldn't just checkpoint the register state alone, so this should get + // moved to protected. To do that ThreadStateCheckpoint needs to be returned as a token... + class RegisterCheckpoint + { + public: + + RegisterCheckpoint() : + m_stack_id (), + m_data_sp () + { + } + + RegisterCheckpoint (const StackID &stack_id) : + m_stack_id (stack_id), + m_data_sp () + { + } + + ~RegisterCheckpoint() + { + } + + const RegisterCheckpoint& + operator= (const RegisterCheckpoint &rhs) + { + if (this != &rhs) + { + this->m_stack_id = rhs.m_stack_id; + this->m_data_sp = rhs.m_data_sp; + } + return *this; + } + + RegisterCheckpoint (const RegisterCheckpoint &rhs) : + m_stack_id (rhs.m_stack_id), + m_data_sp (rhs.m_data_sp) + { + } + + const StackID & + GetStackID() + { + return m_stack_id; + } + + void + SetStackID (const StackID &stack_id) + { + m_stack_id = stack_id; + } + + lldb::DataBufferSP & + GetData() + { + return m_data_sp; + } + + const lldb::DataBufferSP & + GetData() const + { + return m_data_sp; + } + + protected: + StackID m_stack_id; + lldb::DataBufferSP m_data_sp; + }; + + struct ThreadStateCheckpoint + { + uint32_t orig_stop_id; // Dunno if I need this yet but it is an interesting bit of data. + lldb::StopInfoSP stop_info_sp; // You have to restore the stop info or you might continue with the wrong signals. + RegisterCheckpoint register_backup; // You need to restore the registers, of course... + uint32_t current_inlined_depth; + lldb::addr_t current_inlined_pc; + }; + + static void + SettingsInitialize (); + + static void + SettingsTerminate (); + + static const ThreadPropertiesSP & + GetGlobalProperties(); + + Thread (Process &process, lldb::tid_t tid); + virtual ~Thread(); + + lldb::ProcessSP + GetProcess() const + { + return m_process_wp.lock(); + } + + int + GetResumeSignal () const + { + return m_resume_signal; + } + + void + SetResumeSignal (int signal) + { + m_resume_signal = signal; + } + + lldb::StateType + GetState() const; + + void + SetState (lldb::StateType state); + + lldb::StateType + GetResumeState () const + { + return m_resume_state; + } + + void + SetResumeState (lldb::StateType state) + { + m_resume_state = state; + } + + // This function is called on all the threads before "ShouldResume" and + // "WillResume" in case a thread needs to change its state before the + // ThreadList polls all the threads to figure out which ones actually + // will get to run and how. + void + SetupForResume (); + + // Do not override this function, it is for thread plan logic only + bool + ShouldResume (lldb::StateType resume_state); + + // Override this to do platform specific tasks before resume. + virtual void + WillResume (lldb::StateType resume_state) + { + } + + // This clears generic thread state after a resume. If you subclass this, + // be sure to call it. + virtual void + DidResume (); + + // This notifies the thread when a private stop occurs. + virtual void + DidStop (); + + virtual void + RefreshStateAfterStop() = 0; + + void + WillStop (); + + bool + ShouldStop (Event *event_ptr); + + Vote + ShouldReportStop (Event *event_ptr); + + Vote + ShouldReportRun (Event *event_ptr); + + void + Flush (); + + // Return whether this thread matches the specification in ThreadSpec. This is a virtual + // method because at some point we may extend the thread spec with a platform specific + // dictionary of attributes, which then only the platform specific Thread implementation + // would know how to match. For now, this just calls through to the ThreadSpec's + // ThreadPassesBasicTests method. + virtual bool + MatchesSpec (const ThreadSpec *spec); + + lldb::StopInfoSP + GetStopInfo (); + + lldb::StopReason + GetStopReason(); + + // This sets the stop reason to a "blank" stop reason, so you can call functions on the thread + // without having the called function run with whatever stop reason you stopped with. + void + SetStopInfoToNothing(); + + bool + ThreadStoppedForAReason (); + + static const char * + RunModeAsCString (lldb::RunMode mode); + + static const char * + StopReasonAsCString (lldb::StopReason reason); + + virtual const char * + GetInfo () + { + return NULL; + } + + virtual const char * + GetName () + { + return NULL; + } + + virtual const char * + GetQueueName () + { + return NULL; + } + + virtual uint32_t + GetStackFrameCount() + { + return GetStackFrameList()->GetNumFrames(); + } + + virtual lldb::StackFrameSP + GetStackFrameAtIndex (uint32_t idx) + { + return GetStackFrameList()->GetFrameAtIndex(idx); + } + + virtual lldb::StackFrameSP + GetFrameWithConcreteFrameIndex (uint32_t unwind_idx); + + bool + DecrementCurrentInlinedDepth() + { + return GetStackFrameList()->DecrementCurrentInlinedDepth(); + } + + uint32_t + GetCurrentInlinedDepth() + { + return GetStackFrameList()->GetCurrentInlinedDepth(); + } + + Error + ReturnFromFrameWithIndex (uint32_t frame_idx, lldb::ValueObjectSP return_value_sp, bool broadcast = false); + + Error + ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast = false); + + virtual lldb::StackFrameSP + GetFrameWithStackID (const StackID &stack_id) + { + if (stack_id.IsValid()) + return GetStackFrameList()->GetFrameWithStackID (stack_id); + return lldb::StackFrameSP(); + } + + uint32_t + GetSelectedFrameIndex () + { + return GetStackFrameList()->GetSelectedFrameIndex(); + } + + lldb::StackFrameSP + GetSelectedFrame () + { + lldb::StackFrameListSP stack_frame_list_sp(GetStackFrameList()); + return stack_frame_list_sp->GetFrameAtIndex (stack_frame_list_sp->GetSelectedFrameIndex()); + } + + uint32_t + SetSelectedFrame (lldb_private::StackFrame *frame, bool broadcast = false); + + + bool + SetSelectedFrameByIndex (uint32_t frame_idx, bool broadcast = false); + + bool + SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_stream); + + void + SetDefaultFileAndLineToSelectedFrame() + { + GetStackFrameList()->SetDefaultFileAndLineToSelectedFrame(); + } + + virtual lldb::RegisterContextSP + GetRegisterContext () = 0; + + virtual lldb::RegisterContextSP + CreateRegisterContextForFrame (StackFrame *frame) = 0; + + virtual void + ClearStackFrames (); + + virtual bool + SetBackingThread (const lldb::ThreadSP &thread_sp) + { + return false; + } + + virtual lldb::ThreadSP + GetBackingThread () const + { + return lldb::ThreadSP(); + } + + virtual void + ClearBackingThread () + { + // Subclasses can use this function if a thread is actually backed by + // another thread. This is currently used for the OperatingSystem plug-ins + // where they might have a thread that is in memory, yet its registers + // are available through the lldb_private::Thread subclass for the current + // lldb_private::Process class. Since each time the process stops the backing + // threads for memory threads can change, we need a way to clear the backing + // thread for all memory threads each time we stop. + } + + void + DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx); + + //------------------------------------------------------------------ + // Thread Plan Providers: + // This section provides the basic thread plans that the Process control + // machinery uses to run the target. ThreadPlan.h provides more details on + // how this mechanism works. + // The thread provides accessors to a set of plans that perform basic operations. + // The idea is that particular Platform plugins can override these methods to + // provide the implementation of these basic operations appropriate to their + // environment. + // + // NB: All the QueueThreadPlanXXX providers return Shared Pointers to + // Thread plans. This is useful so that you can modify the plans after + // creation in ways specific to that plan type. Also, it is often necessary for + // ThreadPlans that utilize other ThreadPlans to implement their task to keep a shared + // pointer to the sub-plan. + // But besides that, the shared pointers should only be held onto by entities who live no longer + // than the thread containing the ThreadPlan. + // FIXME: If this becomes a problem, we can make a version that just returns a pointer, + // which it is clearly unsafe to hold onto, and a shared pointer version, and only allow + // ThreadPlan and Co. to use the latter. That is made more annoying to do because there's + // no elegant way to friend a method to all sub-classes of a given class. + // + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Queues the base plan for a thread. + /// The version returned by Process does some things that are useful, + /// like handle breakpoints and signals, so if you return a plugin specific + /// one you probably want to call through to the Process one for anything + /// your plugin doesn't explicitly handle. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueFundamentalPlan (bool abort_other_plans); + + //------------------------------------------------------------------ + /// Queues the plan used to step over a breakpoint at the current PC of \a thread. + /// The default version returned by Process handles trap based breakpoints, and + /// will disable the breakpoint, single step over it, then re-enable it. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepOverBreakpointPlan (bool abort_other_plans); + + //------------------------------------------------------------------ + /// Queues the plan used to step one instruction from the current PC of \a thread. + /// + /// @param[in] step_over + /// \b true if we step over calls to functions, false if we step in. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepSingleInstruction (bool step_over, + bool abort_other_plans, + bool stop_other_threads); + + //------------------------------------------------------------------ + /// Queues the plan used to step through an address range, stepping over + /// function calls. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] type + /// Type of step to do, only eStepTypeInto and eStepTypeOver are supported by this plan. + /// + /// @param[in] range + /// The address range to step through. + /// + /// @param[in] addr_context + /// When dealing with stepping through inlined functions the current PC is not enough information to know + /// what "step" means. For instance a series of nested inline functions might start at the same address. + // The \a addr_context provides the current symbol context the step + /// is supposed to be out of. + // FIXME: Currently unused. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepOverRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_other_threads); + + //------------------------------------------------------------------ + /// Queues the plan used to step through an address range, stepping into functions. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] type + /// Type of step to do, only eStepTypeInto and eStepTypeOver are supported by this plan. + /// + /// @param[in] range + /// The address range to step through. + /// + /// @param[in] addr_context + /// When dealing with stepping through inlined functions the current PC is not enough information to know + /// what "step" means. For instance a series of nested inline functions might start at the same address. + // The \a addr_context provides the current symbol context the step + /// is supposed to be out of. + // FIXME: Currently unused. + /// + /// @param[in] step_in_target + /// Name if function we are trying to step into. We will step out if we don't land in that function. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @param[in] avoid_code_without_debug_info + /// If \b true we will step out if we step into code with no debug info. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepInRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + const char *step_in_target, + lldb::RunMode stop_other_threads, + bool avoid_code_without_debug_info); + + //------------------------------------------------------------------ + /// Queue the plan used to step out of the function at the current PC of + /// \a thread. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] addr_context + /// When dealing with stepping through inlined functions the current PC is not enough information to know + /// what "step" means. For instance a series of nested inline functions might start at the same address. + // The \a addr_context provides the current symbol context the step + /// is supposed to be out of. + // FIXME: Currently unused. + /// + /// @param[in] first_insn + /// \b true if this is the first instruction of a function. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @param[in] stop_vote + /// @param[in] run_vote + /// See standard meanings for the stop & run votes in ThreadPlan.h. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepOut (bool abort_other_plans, + SymbolContext *addr_context, + bool first_insn, + bool stop_other_threads, + Vote stop_vote, // = eVoteYes, + Vote run_vote, // = eVoteNoOpinion); + uint32_t frame_idx); + + //------------------------------------------------------------------ + /// Gets the plan used to step through the code that steps from a function + /// call site at the current PC into the actual function call. + /// + /// + /// @param[in] return_stack_id + /// The stack id that we will return to (by setting backstop breakpoints on the return + /// address to that frame) if we fail to step through. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepThrough (StackID &return_stack_id, + bool abort_other_plans, + bool stop_other_threads); + + //------------------------------------------------------------------ + /// Gets the plan used to continue from the current PC. + /// This is a simple plan, mostly useful as a backstop when you are continuing + /// for some particular purpose. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @param[in] target_addr + /// The address to which we're running. + /// + /// @param[in] stop_other_threads + /// \b true if we will stop other threads while we single step this one. + /// + /// @return + /// A shared pointer to the newly queued thread plan, or NULL if the plan could not be queued. + //------------------------------------------------------------------ + virtual lldb::ThreadPlanSP + QueueThreadPlanForRunToAddress (bool abort_other_plans, + Address &target_addr, + bool stop_other_threads); + + virtual lldb::ThreadPlanSP + QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others, + uint32_t frame_idx); + + virtual lldb::ThreadPlanSP + QueueThreadPlanForCallFunction (bool abort_other_plans, + Address& function, + lldb::addr_t arg, + bool stop_other_threads, + bool unwind_on_error = false, + bool ignore_breakpoints = true); + + //------------------------------------------------------------------ + // Thread Plan accessors: + //------------------------------------------------------------------ + + //------------------------------------------------------------------ + /// Gets the plan which will execute next on the plan stack. + /// + /// @return + /// A pointer to the next executed plan. + //------------------------------------------------------------------ + ThreadPlan * + GetCurrentPlan (); + + //------------------------------------------------------------------ + /// Unwinds the thread stack for the innermost expression plan currently + /// on the thread plan stack. + /// + /// @return + /// An error if the thread plan could not be unwound. + //------------------------------------------------------------------ + + Error + UnwindInnermostExpression(); + +private: + bool + PlanIsBasePlan (ThreadPlan *plan_ptr); + + void + BroadcastSelectedFrameChange(StackID &new_frame_id); + +public: + + //------------------------------------------------------------------ + /// Gets the outer-most plan that was popped off the plan stack in the + /// most recent stop. Useful for printing the stop reason accurately. + /// + /// @return + /// A pointer to the last completed plan. + //------------------------------------------------------------------ + lldb::ThreadPlanSP + GetCompletedPlan (); + + //------------------------------------------------------------------ + /// Gets the outer-most return value from the completed plans + /// + /// @return + /// A ValueObjectSP, either empty if there is no return value, + /// or containing the return value. + //------------------------------------------------------------------ + lldb::ValueObjectSP + GetReturnValueObject (); + + //------------------------------------------------------------------ + /// Checks whether the given plan is in the completed plans for this + /// stop. + /// + /// @param[in] plan + /// Pointer to the plan you're checking. + /// + /// @return + /// Returns true if the input plan is in the completed plan stack, + /// false otherwise. + //------------------------------------------------------------------ + bool + IsThreadPlanDone (ThreadPlan *plan); + + //------------------------------------------------------------------ + /// Checks whether the given plan is in the discarded plans for this + /// stop. + /// + /// @param[in] plan + /// Pointer to the plan you're checking. + /// + /// @return + /// Returns true if the input plan is in the discarded plan stack, + /// false otherwise. + //------------------------------------------------------------------ + bool + WasThreadPlanDiscarded (ThreadPlan *plan); + + //------------------------------------------------------------------ + /// Queues a generic thread plan. + /// + /// @param[in] plan_sp + /// The plan to queue. + /// + /// @param[in] abort_other_plans + /// \b true if we discard the currently queued plans and replace them with this one. + /// Otherwise this plan will go on the end of the plan stack. + /// + /// @return + /// A pointer to the last completed plan. + //------------------------------------------------------------------ + void + QueueThreadPlan (lldb::ThreadPlanSP &plan_sp, bool abort_other_plans); + + + //------------------------------------------------------------------ + /// Discards the plans queued on the plan stack of the current thread. This is + /// arbitrated by the "Master" ThreadPlans, using the "OkayToDiscard" call. + // But if \a force is true, all thread plans are discarded. + //------------------------------------------------------------------ + void + DiscardThreadPlans (bool force); + + //------------------------------------------------------------------ + /// Discards the plans queued on the plan stack of the current thread up to and + /// including up_to_plan_sp. + // + // @param[in] up_to_plan_sp + // Discard all plans up to and including this one. + //------------------------------------------------------------------ + void + DiscardThreadPlansUpToPlan (lldb::ThreadPlanSP &up_to_plan_sp); + + void + DiscardThreadPlansUpToPlan (ThreadPlan *up_to_plan_ptr); + + //------------------------------------------------------------------ + /// Prints the current plan stack. + /// + /// @param[in] s + /// The stream to which to dump the plan stack info. + /// + //------------------------------------------------------------------ + void + DumpThreadPlans (Stream *s) const; + + virtual bool + CheckpointThreadState (ThreadStateCheckpoint &saved_state); + + virtual bool + RestoreRegisterStateFromCheckpoint (ThreadStateCheckpoint &saved_state); + + virtual bool + RestoreThreadStateFromCheckpoint (ThreadStateCheckpoint &saved_state); + + void + EnableTracer (bool value, bool single_step); + + void + SetTracer (lldb::ThreadPlanTracerSP &tracer_sp); + + //------------------------------------------------------------------ + // Get the thread index ID. The index ID that is guaranteed to not + // be re-used by a process. They start at 1 and increase with each + // new thread. This allows easy command line access by a unique ID + // that is easier to type than the actual system thread ID. + //------------------------------------------------------------------ + uint32_t + GetIndexID () const; + + + //------------------------------------------------------------------ + // The API ID is often the same as the Thread::GetID(), but not in + // all cases. Thread::GetID() is the user visible thread ID that + // clients would want to see. The API thread ID is the thread ID + // that is used when sending data to/from the debugging protocol. + //------------------------------------------------------------------ + virtual lldb::user_id_t + GetProtocolID () const + { + return GetID(); + } + + //------------------------------------------------------------------ + // lldb::ExecutionContextScope pure virtual functions + //------------------------------------------------------------------ + virtual lldb::TargetSP + CalculateTarget (); + + virtual lldb::ProcessSP + CalculateProcess (); + + virtual lldb::ThreadSP + CalculateThread (); + + virtual lldb::StackFrameSP + CalculateStackFrame (); + + virtual void + CalculateExecutionContext (ExecutionContext &exe_ctx); + + lldb::StackFrameSP + GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr); + + size_t + GetStatus (Stream &strm, + uint32_t start_frame, + uint32_t num_frames, + uint32_t num_frames_with_source); + + size_t + GetStackFrameStatus (Stream& strm, + uint32_t first_frame, + uint32_t num_frames, + bool show_frame_info, + uint32_t num_frames_with_source); + + // We need a way to verify that even though we have a thread in a shared + // pointer that the object itself is still valid. Currently this won't be + // the case if DestroyThread() was called. DestroyThread is called when + // a thread has been removed from the Process' thread list. + bool + IsValid () const + { + return !m_destroy_called; + } + + // Sets and returns a valid stop info based on the process stop ID and the + // current thread plan. If the thread stop ID does not match the process' + // stop ID, the private stop reason is not set and an invalid StopInfoSP may + // be returned. + // + // NOTE: This function must be called before the current thread plan is + // moved to the completed plan stack (in Thread::ShouldStop()). + // + // NOTE: If subclasses override this function, ensure they do not overwrite + // the m_actual_stop_info if it is valid. The stop info may be a + // "checkpointed and restored" stop info, so if it is still around it is + // right even if you have not calculated this yourself, or if it disagrees + // with what you might have calculated. + virtual lldb::StopInfoSP + GetPrivateStopInfo (); + + //---------------------------------------------------------------------- + // Ask the thread subclass to set its stop info. + // + // Thread subclasses should call Thread::SetStopInfo(...) with the + // reason the thread stopped. + // + // @return + // True if Thread::SetStopInfo(...) was called, false otherwise. + //---------------------------------------------------------------------- + virtual bool + CalculateStopInfo () = 0; + + //---------------------------------------------------------------------- + // Gets the temporary resume state for a thread. + // + // This value gets set in each thread by complex debugger logic in + // Thread::ShouldResume() and an appropriate thread resume state will get + // set in each thread every time the process is resumed prior to calling + // Process::DoResume(). The lldb_private::Process subclass should adhere + // to the thread resume state request which will be one of: + // + // eStateRunning - thread will resume when process is resumed + // eStateStepping - thread should step 1 instruction and stop when process + // is resumed + // eStateSuspended - thread should not execute any instructions when + // process is resumed + //---------------------------------------------------------------------- + lldb::StateType + GetTemporaryResumeState() const + { + return m_temporary_resume_state; + } + + void + SetStopInfo (const lldb::StopInfoSP &stop_info_sp); + + void + SetShouldReportStop (Vote vote); + +protected: + + friend class ThreadPlan; + friend class ThreadList; + friend class ThreadEventData; + friend class StackFrameList; + friend class StackFrame; + friend class OperatingSystem; + + // This is necessary to make sure thread assets get destroyed while the thread is still in good shape + // to call virtual thread methods. This must be called by classes that derive from Thread in their destructor. + virtual void DestroyThread (); + + void + PushPlan (lldb::ThreadPlanSP &plan_sp); + + void + PopPlan (); + + void + DiscardPlan (); + + ThreadPlan *GetPreviousPlan (ThreadPlan *plan); + + typedef std::vector plan_stack; + + virtual bool + SaveFrameZeroState (RegisterCheckpoint &checkpoint); + + virtual bool + RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint); + + // register_data_sp must be a DataSP passed to ReadAllRegisterValues. + bool + ResetFrameZeroRegisters (lldb::DataBufferSP register_data_sp); + + virtual lldb_private::Unwind * + GetUnwinder (); + + // Check to see whether the thread is still at the last breakpoint hit that stopped it. + virtual bool + IsStillAtLastBreakpointHit(); + + // Some threads are threads that are made up by OperatingSystem plugins that + // are threads that exist and are context switched out into memory. The + // OperatingSystem plug-in need a ways to know if a thread is "real" or made + // up. + virtual bool + IsOperatingSystemPluginThread () const + { + return false; + } + + + lldb::StackFrameListSP + GetStackFrameList (); + + struct ThreadState + { + uint32_t orig_stop_id; + lldb::StopInfoSP stop_info_sp; + RegisterCheckpoint register_backup; + }; + + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + lldb::ProcessWP m_process_wp; ///< The process that owns this thread. + lldb::StopInfoSP m_stop_info_sp; ///< The private stop reason for this thread + uint32_t m_stop_info_stop_id; // This is the stop id for which the StopInfo is valid. Can use this so you know that + // the thread's m_stop_info_sp is current and you don't have to fetch it again + const uint32_t m_index_id; ///< A unique 1 based index assigned to each thread for easy UI/command line access. + lldb::RegisterContextSP m_reg_context_sp; ///< The register context for this thread's current register state. + lldb::StateType m_state; ///< The state of our process. + mutable Mutex m_state_mutex; ///< Multithreaded protection for m_state. + plan_stack m_plan_stack; ///< The stack of plans this thread is executing. + plan_stack m_completed_plan_stack; ///< Plans that have been completed by this stop. They get deleted when the thread resumes. + plan_stack m_discarded_plan_stack; ///< Plans that have been discarded by this stop. They get deleted when the thread resumes. + mutable Mutex m_frame_mutex; ///< Multithreaded protection for m_state. + lldb::StackFrameListSP m_curr_frames_sp; ///< The stack frames that get lazily populated after a thread stops. + lldb::StackFrameListSP m_prev_frames_sp; ///< The previous stack frames from the last time this thread stopped. + int m_resume_signal; ///< The signal that should be used when continuing this thread. + lldb::StateType m_resume_state; ///< This state is used to force a thread to be suspended from outside the ThreadPlan logic. + lldb::StateType m_temporary_resume_state; ///< This state records what the thread was told to do by the thread plan logic for the current resume. + /// It gets set in Thread::ShoudResume. + std::unique_ptr m_unwinder_ap; + bool m_destroy_called; // This is used internally to make sure derived Thread classes call DestroyThread. + LazyBool m_override_should_notify; +private: + //------------------------------------------------------------------ + // For Thread only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (Thread); + +}; + +} // namespace lldb_private + +#endif // liblldb_Thread_h_ diff --git a/include/lldb/Target/ThreadList.h b/include/lldb/Target/ThreadList.h new file mode 100644 index 00000000000..ddf49b002ec --- /dev/null +++ b/include/lldb/Target/ThreadList.h @@ -0,0 +1,163 @@ +//===-- ThreadList.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadList_h_ +#define liblldb_ThreadList_h_ + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" + + +// FIXME: Currently this is a thread list with lots of functionality for use only by +// the process for which this is the thread list. If we ever want a container class +// to hand out that is just a random subset of threads, with iterator functionality, +// then we should make that part a base class, and make a ProcessThreadList for the +// process. +namespace lldb_private { + +class ThreadList +{ +friend class Process; + +public: + + ThreadList (Process *process); + + ThreadList (const ThreadList &rhs); + + ~ThreadList (); + + const ThreadList& + operator = (const ThreadList& rhs); + + uint32_t + GetSize(bool can_update = true); + + void + AddThread (const lldb::ThreadSP &thread_sp); + + // Return the selected thread if there is one. Otherwise, return the thread + // selected at index 0. + lldb::ThreadSP + GetSelectedThread (); + + bool + SetSelectedThreadByID (lldb::tid_t tid, bool notify = false); + + bool + SetSelectedThreadByIndexID (uint32_t index_id, bool notify = false); + + void + Clear(); + + void + Flush(); + + void + Destroy(); + + // Note that "idx" is not the same as the "thread_index". It is a zero + // based index to accessing the current threads, whereas "thread_index" + // is a unique index assigned + lldb::ThreadSP + GetThreadAtIndex (uint32_t idx, bool can_update = true); + + lldb::ThreadSP + FindThreadByID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + FindThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + RemoveThreadByID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update = true); + + lldb::ThreadSP + FindThreadByIndexID (uint32_t index_id, bool can_update = true); + + lldb::ThreadSP + GetThreadSPForThreadPtr (Thread *thread_ptr); + + bool + ShouldStop (Event *event_ptr); + + Vote + ShouldReportStop (Event *event_ptr); + + Vote + ShouldReportRun (Event *event_ptr); + + void + RefreshStateAfterStop (); + + //------------------------------------------------------------------ + /// The thread list asks tells all the threads it is about to resume. + /// If a thread can "resume" without having to resume the target, it + /// will return false for WillResume, and then the process will not be + /// restarted. + /// + /// @return + /// \b true instructs the process to resume normally, + /// \b false means start & stopped events will be generated, but + /// the process will not actually run. The thread must then return + /// the correct StopInfo when asked. + /// + //------------------------------------------------------------------ + bool + WillResume (); + + void + DidResume (); + + void + DidStop (); + + void + DiscardThreadPlans(); + + uint32_t + GetStopID () const; + + void + SetStopID (uint32_t stop_id); + + Mutex & + GetMutex (); + + void + Update (ThreadList &rhs); + +protected: + + void + SetShouldReportStop (Vote vote); + + void + NotifySelectedThreadChanged (lldb::tid_t tid); + + typedef std::vector collection; + //------------------------------------------------------------------ + // Classes that inherit from Process can see and modify these + //------------------------------------------------------------------ + Process *m_process; ///< The process that manages this thread list. + uint32_t m_stop_id; ///< The process stop ID that this thread list is valid for. + collection m_threads; ///< The threads for this process. + lldb::tid_t m_selected_tid; ///< For targets that need the notion of a current thread. + +private: + ThreadList (); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadList_h_ diff --git a/include/lldb/Target/ThreadPlan.h b/include/lldb/Target/ThreadPlan.h new file mode 100644 index 00000000000..3c83fd1b963 --- /dev/null +++ b/include/lldb/Target/ThreadPlan.h @@ -0,0 +1,658 @@ +//===-- ThreadPlan.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlan_h_ +#define liblldb_ThreadPlan_h_ + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/UserID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanTracer.h" +#include "lldb/Target/StopInfo.h" + +namespace lldb_private { + +//------------------------------------------------------------------ +// ThreadPlan: +// This is the pure virtual base class for thread plans. +// +// The thread plans provide the "atoms" of behavior that +// all the logical process control, either directly from commands or through +// more complex composite plans will rely on. +// +// Plan Stack: +// +// The thread maintaining a thread plan stack, and you program the actions of a particular thread +// by pushing plans onto the plan stack. +// There is always a "Current" plan, which is the head of the plan stack, though in some cases +// a plan may defer to plans higher in the stack for some piece of information. +// +// The plan stack is never empty, there is always a Base Plan which persists through the life +// of the running process. +// +// +// Creating Plans: +// +// The thread plan is generally created and added to the plan stack through the QueueThreadPlanFor... API +// in lldb::Thread. Those API's will return the plan that performs the named operation in a manner +// appropriate for the current process. The plans in lldb/source/Target are generic +// implementations, but a Process plugin can override them. +// +// ValidatePlan is then called. If it returns false, the plan is unshipped. This is a little +// convenience which keeps us from having to error out of the constructor. +// +// Then the plan is added to the plan stack. When the plan is added to the plan stack its DidPush +// will get called. This is useful if a plan wants to push any additional plans as it is constructed, +// since you need to make sure you're already on the stack before you push additional plans. +// +// Completed Plans: +// +// When the target process stops the plans are queried, among other things, for whether their job is done. +// If it is they are moved from the plan stack to the Completed Plan stack in reverse order from their position +// on the plan stack (since multiple plans may be done at a given stop.) This is used primarily so that +// the lldb::Thread::StopInfo for the thread can be set properly. If one plan pushes another to achieve part of +// its job, but it doesn't want that sub-plan to be the one that sets the StopInfo, then call SetPrivate on the +// sub-plan when you create it, and the Thread will pass over that plan in reporting the reason for the stop. +// +// Discarded plans: +// +// Your plan may also get discarded, i.e. moved from the plan stack to the "discarded plan stack". This can +// happen, for instance, if the plan is calling a function and the function call crashes and you want +// to unwind the attempt to call. So don't assume that your plan will always successfully stop. Which leads to: +// +// Cleaning up after your plans: +// +// When the plan is moved from the plan stack its WillPop method is always called, no matter why. Once it is +// moved off the plan stack it is done, and won't get a chance to run again. So you should +// undo anything that affects target state in this method. But be sure to leave the plan able to correctly +// fill the StopInfo, however. +// N.B. Don't wait to do clean up target state till the destructor, since that will usually get called when +// the target resumes, and you want to leave the target state correct for new plans in the time between when +// your plan gets unshipped and the next resume. +// +// Over the lifetime of the plan, various methods of the ThreadPlan are then called in response to changes of state in +// the process we are debugging as follows: +// +// Resuming: +// +// When the target process is about to be restarted, the plan's WillResume method is called, +// giving the plan a chance to prepare for the run. If WillResume returns false, then the +// process is not restarted. Be sure to set an appropriate error value in the Process if +// you have to do this. Note, ThreadPlans actually implement DoWillResume, WillResume wraps that call. +// +// Next the "StopOthers" method of all the threads are polled, and if one thread's Current plan +// returns "true" then only that thread gets to run. If more than one returns "true" the threads that want to run solo +// get run one by one round robin fashion. Otherwise all are let to run. +// +// Note, the way StopOthers is implemented, the base class implementation just asks the previous plan. So if your plan +// has no opinion about whether it should run stopping others or not, just don't implement StopOthers, and the parent +// will be asked. +// +// Finally, for each thread that is running, it run state is set to the return of RunState from the +// thread's Current plan. +// +// Responding to a stop: +// +// When the target process stops, the plan is called in the following stages: +// +// First the thread asks the Current Plan if it can handle this stop by calling PlanExplainsStop. +// If the Current plan answers "true" then it is asked if the stop should percolate all the way to the +// user by calling the ShouldStop method. If the current plan doesn't explain the stop, then we query down +// the plan stack for a plan that does explain the stop. The plan that does explain the stop then needs to +// figure out what to do about the plans below it in the stack. If the stop is recoverable, then the plan that +// understands it can just do what it needs to set up to restart, and then continue. +// Otherwise, the plan that understood the stop should call DiscardPlanStack to clean up the stack below it. +// Note, plans actually implement DoPlanExplainsStop, the result is cached in PlanExplainsStop so the DoPlanExplainsStop +// itself will only get called once per stop. +// +// Master plans: +// +// In the normal case, when we decide to stop, we will collapse the plan stack up to the point of the plan that understood +// the stop reason. However, if a plan wishes to stay on the stack after an event it didn't directly handle +// it can designate itself a "Master" plan by responding true to IsMasterPlan, and then if it wants not to be +// discarded, it can return true to OkayToDiscard, and it and all its dependent plans will be preserved when +// we resume execution. +// +// The other effect of being a master plan is that when the Master plan is done , if it has set "OkayToDiscard" to false, +// then it will be popped & execution will stop and return to the user. Remember that if OkayToDiscard is false, the +// plan will be popped and control will be given to the next plan above it on the stack So setting OkayToDiscard to +// false means the user will regain control when the MasterPlan is completed. +// +// Between these two controls this allows things like: a MasterPlan/DontDiscard Step Over to hit a breakpoint, stop and +// return control to the user, but then when the user continues, the step out succeeds. +// Even more tricky, when the breakpoint is hit, the user can continue to step in/step over/etc, and finally when they +// continue, they will finish up the Step Over. +// +// FIXME: MasterPlan & OkayToDiscard aren't really orthogonal. MasterPlan designation means that this plan controls +// it's fate and the fate of plans below it. OkayToDiscard tells whether the MasterPlan wants to stay on the stack. I +// originally thought "MasterPlan-ness" would need to be a fixed characteristic of a ThreadPlan, in which case you needed +// the extra control. But that doesn't seem to be true. So we should be able to convert to only MasterPlan status to mean +// the current "MasterPlan/DontDiscard". Then no plans would be MasterPlans by default, and you would set the ones you +// wanted to be "user level" in this way. +// +// +// Actually Stopping: +// +// If a plan says responds "true" to ShouldStop, then it is asked if it's job is complete by calling +// MischiefManaged. If that returns true, the thread is popped from the plan stack and added to the +// Completed Plan Stack. Then the next plan in the stack is asked if it ShouldStop, and it returns "true", +// it is asked if it is done, and if yes popped, and so on till we reach a plan that is not done. +// +// Since you often know in the ShouldStop method whether your plan is complete, as a convenience you can call +// SetPlanComplete and the ThreadPlan implementation of MischiefManaged will return "true", without your having +// to redo the calculation when your sub-classes MischiefManaged is called. If you call SetPlanComplete, you can +// later use IsPlanComplete to determine whether the plan is complete. This is only a convenience for sub-classes, +// the logic in lldb::Thread will only call MischiefManaged. +// +// One slightly tricky point is you have to be careful using SetPlanComplete in PlanExplainsStop because you +// are not guaranteed that PlanExplainsStop for a plan will get called before ShouldStop gets called. If your sub-plan +// explained the stop and then popped itself, only your ShouldStop will get called. +// +// If ShouldStop for any thread returns "true", then the WillStop method of the Current plan of +// all threads will be called, the stop event is placed on the Process's public broadcaster, and +// control returns to the upper layers of the debugger. +// +// Reporting the stop: +// +// When the process stops, the thread is given a StopReason, in the form of a StopInfo object. If there is a completed +// plan corresponding to the stop, then the "actual" stop reason will be suppressed, and instead a StopInfoThreadPlan +// object will be cons'ed up from the highest completed plan in the stack. However, if the plan doesn't want to be +// the stop reason, then it can call SetPlanComplete and pass in "false" for the "success" parameter. In that case, +// the real stop reason will be used instead. One exapmle of this is the "StepRangeStepIn" thread plan. If it stops +// because of a crash or breakpoint hit, it wants to unship itself, because it isn't so useful to have step in keep going +// after a breakpoint hit. But it can't be the reason for the stop or no-one would see that they had hit a breakpoint. +// +// Cleaning up the plan stack: +// +// One of the complications of MasterPlans is that you may get past the limits of a plan without triggering it to clean +// itself up. For instance, if you are doing a MasterPlan StepOver, and hit a breakpoint in a called function, then +// step over enough times to step out of the initial StepOver range, each of the step overs will explain the stop & +// take themselves off the stack, but control would never be returned to the original StepOver. Eventually, the user +// will continue, and when that continue stops, the old stale StepOver plan that was left on the stack will get woken +// up and notice it is done. But that can leave junk on the stack for a while. To avoid that, the plans implement a +// "IsPlanStale" method, that can check whether it is relevant anymore. On stop, after the regular plan negotiation, +// the remaining plan stack is consulted and if any plan says it is stale, it and the plans below it are discarded from +// the stack. +// +// Automatically Resuming: +// +// If ShouldStop for all threads returns "false", then the target process will resume. This then cycles back to +// Resuming above. +// +// Reporting eStateStopped events when the target is restarted: +// +// If a plan decides to auto-continue the target by returning "false" from ShouldStop, then it will be asked +// whether the Stopped event should still be reported. For instance, if you hit a breakpoint that is a User set +// breakpoint, but the breakpoint callback said to continue the target process, you might still want to inform +// the upper layers of lldb that the stop had happened. +// The way this works is every thread gets to vote on whether to report the stop. If all votes are eVoteNoOpinion, +// then the thread list will decide what to do (at present it will pretty much always suppress these stopped events.) +// If there is an eVoteYes, then the event will be reported regardless of the other votes. If there is an eVoteNo +// and no eVoteYes's, then the event won't be reported. +// +// One other little detail here, sometimes a plan will push another plan onto the plan stack to do some part of +// the first plan's job, and it would be convenient to tell that plan how it should respond to ShouldReportStop. +// You can do that by setting the stop_vote in the child plan when you create it. +// +// Suppressing the initial eStateRunning event: +// +// The private process running thread will take care of ensuring that only one "eStateRunning" event will be +// delivered to the public Process broadcaster per public eStateStopped event. However there are some cases +// where the public state of this process is eStateStopped, but a thread plan needs to restart the target, but +// doesn't want the running event to be publically broadcast. The obvious example of this is running functions +// by hand as part of expression evaluation. To suppress the running event return eVoteNo from ShouldReportStop, +// to force a running event to be reported return eVoteYes, in general though you should return eVoteNoOpinion +// which will allow the ThreadList to figure out the right thing to do. +// The run_vote argument to the constructor works like stop_vote, and is a way for a plan to instruct a sub-plan +// on how to respond to ShouldReportStop. +// +//------------------------------------------------------------------ + +class ThreadPlan : + public UserID +{ +public: + typedef enum + { + eAllThreads, + eSomeThreads, + eThisThread + } ThreadScope; + + // We use these enums so that we can cast a base thread plan to it's real type without having to resort + // to dynamic casting. + typedef enum + { + eKindGeneric, + eKindNull, + eKindBase, + eKindCallFunction, + eKindStepInstruction, + eKindStepOut, + eKindStepOverBreakpoint, + eKindStepOverRange, + eKindStepInRange, + eKindRunToAddress, + eKindStepThrough, + eKindStepUntil, + eKindTestCondition + + } ThreadPlanKind; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlan (ThreadPlanKind kind, + const char *name, + Thread &thread, + Vote stop_vote, + Vote run_vote); + + virtual + ~ThreadPlan(); + + //------------------------------------------------------------------ + /// Returns the name of this thread plan. + /// + /// @return + /// A const char * pointer to the thread plan's name. + //------------------------------------------------------------------ + const char * + GetName () const + { + return m_name.c_str(); + } + + //------------------------------------------------------------------ + /// Returns the Thread that is using this thread plan. + /// + /// @return + /// A pointer to the thread plan's owning thread. + //------------------------------------------------------------------ + Thread & + GetThread() + { + return m_thread; + } + + const Thread & + GetThread() const + { + return m_thread; + } + + Target & + GetTarget() + { + return m_thread.GetProcess()->GetTarget(); + } + + const Target & + GetTarget() const + { + return m_thread.GetProcess()->GetTarget(); + } + + //------------------------------------------------------------------ + /// Print a description of this thread to the stream \a s. + /// \a thread. + /// + /// @param[in] s + /// The stream to which to print the description. + /// + /// @param[in] level + /// The level of description desired. Note that eDescriptionLevelBrief + /// will be used in the stop message printed when the plan is complete. + //------------------------------------------------------------------ + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level) = 0; + + //------------------------------------------------------------------ + /// Returns whether this plan could be successfully created. + /// + /// @param[in] error + /// A stream to which to print some reason why the plan could not be created. + /// Can be NULL. + /// + /// @return + /// \b true if the plan should be queued, \b false otherwise. + //------------------------------------------------------------------ + virtual bool + ValidatePlan (Stream *error) = 0; + + bool + TracerExplainsStop () + { + if (!m_tracer_sp) + return false; + else + return m_tracer_sp->TracerExplainsStop(); + } + + + lldb::StateType + RunState (); + + bool + PlanExplainsStop (Event *event_ptr); + + virtual bool + ShouldStop (Event *event_ptr) = 0; + + virtual bool + ShouldAutoContinue (Event *event_ptr) + { + return false; + } + + // Whether a "stop class" event should be reported to the "outside world". In general + // if a thread plan is active, events should not be reported. + + virtual Vote + ShouldReportStop (Event *event_ptr); + + virtual Vote + ShouldReportRun (Event *event_ptr); + + virtual void + SetStopOthers (bool new_value); + + virtual bool + StopOthers (); + + // This is the wrapper for DoWillResume that does generic ThreadPlan logic, then + // calls DoWillResume. + bool + WillResume (lldb::StateType resume_state, bool current_plan); + + virtual bool + WillStop () = 0; + + bool + IsMasterPlan() + { + return m_is_master_plan; + } + + bool + SetIsMasterPlan (bool value) + { + bool old_value = m_is_master_plan; + m_is_master_plan = value; + return old_value; + } + + virtual bool + OkayToDiscard(); + + void + SetOkayToDiscard (bool value) + { + m_okay_to_discard = value; + } + + // The base class MischiefManaged does some cleanup - so you have to call it + // in your MischiefManaged derived class. + virtual bool + MischiefManaged (); + + virtual void + ThreadDestroyed () + { + // Any cleanup that a plan might want to do in case the thread goes away + // in the middle of the plan being queued on a thread can be done here. + } + + bool + GetPrivate () + { + return m_plan_private; + } + + void + SetPrivate (bool input) + { + m_plan_private = input; + } + + virtual void + DidPush(); + + virtual void + WillPop(); + + // This pushes a plan onto the plan stack of the current plan's thread. + void + PushPlan (lldb::ThreadPlanSP &thread_plan_sp) + { + m_thread.PushPlan (thread_plan_sp); + } + + ThreadPlanKind GetKind() const + { + return m_kind; + } + + bool + IsPlanComplete(); + + void + SetPlanComplete (bool success = true); + + virtual bool + IsPlanStale () + { + return false; + } + + bool + PlanSucceeded () + { + return m_plan_succeeded; + } + + virtual bool + IsBasePlan() + { + return false; + } + + lldb::ThreadPlanTracerSP & + GetThreadPlanTracer() + { + return m_tracer_sp; + } + + void + SetThreadPlanTracer (lldb::ThreadPlanTracerSP new_tracer_sp) + { + m_tracer_sp = new_tracer_sp; + } + + void + DoTraceLog () + { + if (m_tracer_sp && m_tracer_sp->TracingEnabled()) + m_tracer_sp->Log(); + } + + // Some thread plans hide away the actual stop info which caused any particular stop. For + // instance the ThreadPlanCallFunction restores the original stop reason so that stopping and + // calling a few functions won't lose the history of the run. + // This call can be implemented to get you back to the real stop info. + virtual lldb::StopInfoSP + GetRealStopInfo () + { + return m_thread.GetStopInfo (); + } + + virtual lldb::ValueObjectSP + GetReturnValueObject () + { + return lldb::ValueObjectSP(); + } + + // If a thread plan stores the state before it was run, then you might + // want to restore the state when it is done. This will do that job. + // This is mostly useful for artificial plans like CallFunction plans. + + virtual bool + RestoreThreadState() + { + // Nothing to do in general. + return true; + } + + virtual bool + IsVirtualStep() + { + return false; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlan can see and modify these + //------------------------------------------------------------------ + + virtual bool + DoWillResume (lldb::StateType resume_state, bool current_plan) { return true; }; + + virtual bool + DoPlanExplainsStop (Event *event_ptr) = 0; + + // This gets the previous plan to the current plan (for forwarding requests). + // This is mostly a formal requirement, it allows us to make the Thread's + // GetPreviousPlan protected, but only friend ThreadPlan to thread. + + ThreadPlan * + GetPreviousPlan () + { + return m_thread.GetPreviousPlan (this); + } + + // This forwards the private Thread::GetPrivateStopInfo which is generally what + // ThreadPlan's need to know. + + lldb::StopInfoSP + GetPrivateStopInfo() + { + return m_thread.GetPrivateStopInfo (); + } + + void + SetStopInfo (lldb::StopInfoSP stop_reason_sp) + { + m_thread.SetStopInfo (stop_reason_sp); + } + + void + CachePlanExplainsStop (bool does_explain) + { + m_cached_plan_explains_stop = does_explain ? eLazyBoolYes : eLazyBoolNo; + } + + LazyBool + GetCachedPlanExplainsStop () const + { + return m_cached_plan_explains_stop; + } + + virtual lldb::StateType + GetPlanRunState () = 0; + + Thread &m_thread; + Vote m_stop_vote; + Vote m_run_vote; + +private: + //------------------------------------------------------------------ + // For ThreadPlan only + //------------------------------------------------------------------ + static lldb::user_id_t GetNextID (); + + ThreadPlanKind m_kind; + std::string m_name; + Mutex m_plan_complete_mutex; + LazyBool m_cached_plan_explains_stop; + bool m_plan_complete; + bool m_plan_private; + bool m_okay_to_discard; + bool m_is_master_plan; + bool m_plan_succeeded; + + lldb::ThreadPlanTracerSP m_tracer_sp; + +private: + DISALLOW_COPY_AND_ASSIGN(ThreadPlan); +}; + +//---------------------------------------------------------------------- +// ThreadPlanNull: +// Threads are assumed to always have at least one plan on the plan stack. +// This is put on the plan stack when a thread is destroyed so that if you +// accidentally access a thread after it is destroyed you won't crash. +// But asking questions of the ThreadPlanNull is definitely an error. +//---------------------------------------------------------------------- + +class ThreadPlanNull : public ThreadPlan +{ +public: + ThreadPlanNull (Thread &thread); + virtual ~ThreadPlanNull (); + + virtual void + GetDescription (Stream *s, + lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual bool + MischiefManaged (); + + virtual bool + WillStop (); + + virtual bool + IsBasePlan() + { + return true; + } + + virtual bool + OkayToDiscard () + { + return false; + } + +protected: + virtual bool + DoPlanExplainsStop (Event *event_ptr); + + virtual lldb::StateType + GetPlanRunState (); + +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlan_h_ diff --git a/include/lldb/Target/ThreadPlanBase.h b/include/lldb/Target/ThreadPlanBase.h new file mode 100644 index 00000000000..69959e12f84 --- /dev/null +++ b/include/lldb/Target/ThreadPlanBase.h @@ -0,0 +1,71 @@ +//===-- ThreadPlanBase.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanFundamental_h_ +#define liblldb_ThreadPlanFundamental_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + + +//------------------------------------------------------------------ +// Base thread plans: +// This is the generic version of the bottom most plan on the plan stack. It should +// be able to handle generic breakpoint hitting, and signals and exceptions. +//------------------------------------------------------------------ + +class ThreadPlanBase : public ThreadPlan +{ +friend class Process; // RunThreadPlan manages "stopper" base plans. +public: + virtual ~ThreadPlanBase (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual Vote ShouldReportStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + + virtual bool OkayToDiscard() + { + return false; + } + + virtual bool + IsBasePlan() + { + return true; + } + +protected: + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + virtual bool DoPlanExplainsStop (Event *event_ptr); + ThreadPlanBase (Thread &thread); + +private: + friend lldb::ThreadPlanSP + Thread::QueueFundamentalPlan(bool abort_other_plans); + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanBase); +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanFundamental_h_ diff --git a/include/lldb/Target/ThreadPlanCallFunction.h b/include/lldb/Target/ThreadPlanCallFunction.h new file mode 100644 index 00000000000..d747706c639 --- /dev/null +++ b/include/lldb/Target/ThreadPlanCallFunction.h @@ -0,0 +1,193 @@ +//===-- ThreadPlanCallFunction.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanCallFunction_h_ +#define liblldb_ThreadPlanCallFunction_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanCallFunction : public ThreadPlan +{ + // Create a thread plan to call a function at the address passed in the "function" + // argument. If you plan to call GetReturnValueObject, then pass in the + // return type, otherwise just pass in an invalid ClangASTType. +public: + ThreadPlanCallFunction (Thread &thread, + const Address &function, + const ClangASTType &return_type, + lldb::addr_t arg, + bool stop_other_threads, + bool unwind_on_error = true, + bool ignore_breakpoints = false, + lldb::addr_t *this_arg = 0, + lldb::addr_t *cmd_arg = 0); + + ThreadPlanCallFunction (Thread &thread, + const Address &function, + const ClangASTType &return_type, + bool stop_other_threads, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL); + + virtual + ~ThreadPlanCallFunction (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual Vote + ShouldReportStop(Event *event_ptr); + + virtual bool + StopOthers (); + + virtual void + SetStopOthers (bool new_value); + + virtual lldb::StateType + GetPlanRunState (); + + virtual void + DidPush (); + + virtual bool + WillStop (); + + virtual bool + MischiefManaged (); + + // To get the return value from a function call you must create a + // lldb::ValueSP that contains a valid clang type in its context and call + // RequestReturnValue. The ValueSP will be stored and when the function is + // done executing, the object will check if there is a requested return + // value. If there is, the return value will be retrieved using the + // ABI::GetReturnValue() for the ABI in the process. Then after the thread + // plan is complete, you can call "GetReturnValue()" to retrieve the value + // that was extracted. + + virtual lldb::ValueObjectSP + GetReturnValueObject () + { + return m_return_valobj_sp; + } + + // Return the stack pointer that the function received + // on entry. Any stack address below this should be + // considered invalid after the function has been + // cleaned up. + lldb::addr_t + GetFunctionStackPointer() + { + return m_function_sp; + } + + // Classes that derive from ClangFunction, and implement + // their own WillPop methods should call this so that the + // thread state gets restored if the plan gets discarded. + virtual void + WillPop (); + + // If the thread plan stops mid-course, this will be the stop reason that interrupted us. + // Once DoTakedown is called, this will be the real stop reason at the end of the function call. + // If it hasn't been set for one or the other of these reasons, we'll return the PrivateStopReason. + // This is needed because we want the CallFunction thread plans not to show up as the stop reason. + // But if something bad goes wrong, it is nice to be able to tell the user what really happened. + + virtual lldb::StopInfoSP + GetRealStopInfo() + { + if (m_real_stop_info_sp) + return m_real_stop_info_sp; + else + return GetPrivateStopInfo (); + } + + lldb::addr_t + GetStopAddress () + { + return m_stop_address; + } + + virtual bool + RestoreThreadState(); + +protected: + void ReportRegisterState (const char *message); + + virtual bool + DoPlanExplainsStop (Event *event_ptr); + +private: + + bool + ConstructorSetup (Thread &thread, + ABI *& abi, + lldb::addr_t &start_load_addr, + lldb::addr_t &function_load_addr); + + void + DoTakedown (bool success); + + void + SetBreakpoints (); + + void + ClearBreakpoints (); + + bool + BreakpointsExplainStop (); + + bool m_valid; + bool m_stop_other_threads; + Address m_function_addr; + Address m_start_addr; + lldb::addr_t m_function_sp; + Thread::RegisterCheckpoint m_register_backup; + lldb::ThreadPlanSP m_subplan_sp; + LanguageRuntime *m_cxx_language_runtime; + LanguageRuntime *m_objc_language_runtime; + Thread::ThreadStateCheckpoint m_stored_thread_state; + lldb::StopInfoSP m_real_stop_info_sp; // In general we want to hide call function + // thread plans, but for reporting purposes, + // it's nice to know the real stop reason. + // This gets set in DoTakedown. + StreamString m_constructor_errors; + ClangASTType m_return_type; + lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, use the ABI to extract values when complete + bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that. + lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown; + bool m_unwind_on_error; + bool m_ignore_breakpoints; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanCallFunction_h_ diff --git a/include/lldb/Target/ThreadPlanCallUserExpression.h b/include/lldb/Target/ThreadPlanCallUserExpression.h new file mode 100644 index 00000000000..7a7ec33049e --- /dev/null +++ b/include/lldb/Target/ThreadPlanCallUserExpression.h @@ -0,0 +1,65 @@ +//===-- ThreadPlanCallUserExpression.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanCallUserExpression_h_ +#define liblldb_ThreadPlanCallUserExpression_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanCallFunction.h" + +namespace lldb_private { + +class ThreadPlanCallUserExpression : public ThreadPlanCallFunction +{ +public: + ThreadPlanCallUserExpression (Thread &thread, + Address &function, + lldb::addr_t arg, + bool stop_other_threads, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg, + lldb::addr_t *cmd_arg, + ClangUserExpression::ClangUserExpressionSP &user_expression_sp); + + virtual + ~ThreadPlanCallUserExpression (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual void + WillPop () + { + ThreadPlanCallFunction::WillPop(); + if (m_user_expression_sp) + m_user_expression_sp.reset(); + } + + virtual lldb::StopInfoSP + GetRealStopInfo(); + +protected: +private: + ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; // This is currently just used to ensure the + // User expression the initiated this ThreadPlan + // lives as long as the thread plan does. + DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallUserExpression); +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanCallUserExpression_h_ diff --git a/include/lldb/Target/ThreadPlanRunToAddress.h b/include/lldb/Target/ThreadPlanRunToAddress.h new file mode 100644 index 00000000000..d9482066801 --- /dev/null +++ b/include/lldb/Target/ThreadPlanRunToAddress.h @@ -0,0 +1,85 @@ +//===-- ThreadPlanRunToAddress.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanRunToAddress_h_ +#define liblldb_ThreadPlanRunToAddress_h_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanRunToAddress : public ThreadPlan +{ +public: + ThreadPlanRunToAddress (Thread &thread, + Address &address, + bool stop_others); + + ThreadPlanRunToAddress (Thread &thread, + lldb::addr_t address, + bool stop_others); + + ThreadPlanRunToAddress (Thread &thread, + const std::vector &addresses, + bool stop_others); + + + virtual + ~ThreadPlanRunToAddress (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ValidatePlan (Stream *error); + + virtual bool + ShouldStop (Event *event_ptr); + + virtual bool + StopOthers (); + + virtual void + SetStopOthers (bool new_value); + + virtual lldb::StateType + GetPlanRunState (); + + virtual bool + WillStop (); + + virtual bool + MischiefManaged (); + +protected: + virtual bool + DoPlanExplainsStop (Event *event_ptr); + + void SetInitialBreakpoints(); + bool AtOurAddress(); +private: + bool m_stop_others; + std::vector m_addresses; // This is the address we are going to run to. + // TODO: Would it be useful to have multiple addresses? + std::vector m_break_ids; // This is the breakpoint we are using to stop us at m_address. + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanRunToAddress); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanRunToAddress_h_ diff --git a/include/lldb/Target/ThreadPlanShouldStopHere.h b/include/lldb/Target/ThreadPlanShouldStopHere.h new file mode 100644 index 00000000000..62068b78ae4 --- /dev/null +++ b/include/lldb/Target/ThreadPlanShouldStopHere.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanShouldStopHere.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanShouldStopHere_h_ +#define liblldb_ThreadPlanShouldStopHere_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +// This is an interface that ThreadPlans can adopt to allow flexible modifications of the behavior +// when a thread plan comes to a place where it would ordinarily stop. If such modification makes +// sense for your plan, inherit from this class, and when you would be about to stop (in your ShouldStop +// method), call InvokeShouldStopHereCallback, and if that returns a non-NULL plan, execute that +// plan instead of stopping. +// +// The classic example of the use of this is ThreadPlanStepInRange not stopping in frames that have +// no debug information. +// +// This class also defines a set of flags to control general aspects of this "ShouldStop" behavior. +// A class implementing this protocol needs to define a default set of flags, and can provide access to +// changing that default flag set if it wishes. + +class ThreadPlanShouldStopHere +{ +public: + enum + { + eNone = 0, + eAvoidInlines = (1 << 0), + eAvoidNoDebug = (1 << 1) + }; + + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + ThreadPlanShouldStopHere (ThreadPlan *owner, + ThreadPlanShouldStopHereCallback callback = NULL, + void *baton = NULL); + virtual + ~ThreadPlanShouldStopHere(); + + void + SetShouldStopHereCallback (ThreadPlanShouldStopHereCallback callback, void *baton); + + lldb::ThreadPlanSP + InvokeShouldStopHereCallback (); + + lldb_private::Flags & + GetFlags () + { + return m_flags; + } + + const lldb_private::Flags & + GetFlags () const + { + return m_flags; + } + +protected: + // Implement this, and call it in the plan's constructor to set the default flags. + virtual void SetFlagsToDefault () = 0; + + //------------------------------------------------------------------ + // Classes that inherit from ThreadPlanShouldStopHere can see and modify these + //------------------------------------------------------------------ + ThreadPlanShouldStopHereCallback m_callback; + void * m_baton; + ThreadPlan *m_owner; + lldb_private::Flags m_flags; + +private: + //------------------------------------------------------------------ + // For ThreadPlanShouldStopHere only + //------------------------------------------------------------------ + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanShouldStopHere); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanShouldStopHere_h_ diff --git a/include/lldb/Target/ThreadPlanStepInRange.h b/include/lldb/Target/ThreadPlanStepInRange.h new file mode 100644 index 00000000000..dbc8446b2e1 --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepInRange.h @@ -0,0 +1,110 @@ +//===-- ThreadPlanStepInRange.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepInRange_h_ +#define liblldb_ThreadPlanStepInRange_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanShouldStopHere.h" + +namespace lldb_private { + +class ThreadPlanStepInRange : + public ThreadPlanStepRange, + public ThreadPlanShouldStopHere +{ +public: + ThreadPlanStepInRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + ThreadPlanStepInRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + const char *step_into_function_name, + lldb::RunMode stop_others); + + virtual + ~ThreadPlanStepInRange (); + + virtual void + GetDescription (Stream *s, lldb::DescriptionLevel level); + + virtual bool + ShouldStop (Event *event_ptr); + + void SetAvoidRegexp(const char *name); + + void SetStepInTarget (const char *target) + { + m_step_into_target.SetCString(target); + } + + static lldb::ThreadPlanSP + DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton); + + static void + SetDefaultFlagValue (uint32_t new_value); + + bool + IsVirtualStep(); + +protected: + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + + virtual bool + DoPlanExplainsStop (Event *event_ptr); + + virtual void + SetFlagsToDefault (); + + bool + FrameMatchesAvoidRegexp (); + +private: + + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepOverRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepInRange (bool abort_other_plans, + const AddressRange &range, + const SymbolContext &addr_context, + const char *step_in_target, + lldb::RunMode stop_others, + bool avoid_code_without_debug_info); + + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + static uint32_t s_default_flag_values; + lldb::ThreadPlanSP m_sub_plan_sp; // Keep track of the last plan we were running. If it fails, we should stop. + std::unique_ptr m_avoid_regexp_ap; + bool m_step_past_prologue; // FIXME: For now hard-coded to true, we could put a switch in for this if there's + // demand for that. + bool m_virtual_step; // true if we've just done a "virtual step", i.e. just moved the inline stack depth. + ConstString m_step_into_target; + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepInRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepInRange_h_ diff --git a/include/lldb/Target/ThreadPlanStepInstruction.h b/include/lldb/Target/ThreadPlanStepInstruction.h new file mode 100644 index 00000000000..eb4a64bcbc8 --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepInstruction.h @@ -0,0 +1,64 @@ +//===-- ThreadPlanStepInstruction.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepInstruction_h_ +#define liblldb_ThreadPlanStepInstruction_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepInstruction : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepInstruction (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + + ThreadPlanStepInstruction (Thread &thread, + bool step_over, + bool stop_others, + Vote stop_vote, + Vote run_vote); + +private: + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepSingleInstruction (bool step_over, bool abort_other_plans, bool stop_other_threads); + + lldb::addr_t m_instruction_addr; + bool m_stop_other_threads; + bool m_step_over; + // These two are used only for the step over case. + bool m_start_has_symbol; + StackID m_stack_id; + StackID m_parent_frame_id; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepInstruction); + +}; + + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepInstruction_h_ diff --git a/include/lldb/Target/ThreadPlanStepOut.h b/include/lldb/Target/ThreadPlanStepOut.h new file mode 100644 index 00000000000..2737978a4ed --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepOut.h @@ -0,0 +1,90 @@ +//===-- ThreadPlanStepOut.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepOut_h_ +#define liblldb_ThreadPlanStepOut_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepOut : public ThreadPlan +{ +public: + ThreadPlanStepOut (Thread &thread, + SymbolContext *addr_context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote, + uint32_t frame_idx); + + virtual ~ThreadPlanStepOut (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void DidPush(); + virtual bool IsPlanStale(); + + virtual lldb::ValueObjectSP GetReturnValueObject() + { + return m_return_valobj_sp; + } + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + bool QueueInlinedStepPlan (bool queue_now); + +private: + SymbolContext *m_step_from_context; + lldb::addr_t m_step_from_insn; + StackID m_step_out_to_id; + StackID m_immediate_step_from_id; + lldb::break_id_t m_return_bp_id; + lldb::addr_t m_return_addr; + bool m_first_insn; + bool m_stop_others; + lldb::ThreadPlanSP m_step_through_inline_plan_sp; + lldb::ThreadPlanSP m_step_out_plan_sp; + Function *m_immediate_step_from_function; + lldb::ValueObjectSP m_return_valobj_sp; + + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepOut (bool abort_other_plans, + SymbolContext *addr_context, + bool first_insn, + bool stop_others, + Vote stop_vote, + Vote run_vote, + uint32_t frame_idx); + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + void + CalculateReturnValue(); + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOut); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOut_h_ diff --git a/include/lldb/Target/ThreadPlanStepOverBreakpoint.h b/include/lldb/Target/ThreadPlanStepOverBreakpoint.h new file mode 100644 index 00000000000..41cac5c9b0b --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepOverBreakpoint.h @@ -0,0 +1,57 @@ +//===-- ThreadPlanStepOverBreakpoint.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepOverBreakpoint_h_ +#define liblldb_ThreadPlanStepOverBreakpoint_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepOverBreakpoint : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepOverBreakpoint (); + + ThreadPlanStepOverBreakpoint (Thread &thread); + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void ThreadDestroyed (); + void SetAutoContinue (bool do_it); + virtual bool ShouldAutoContinue(Event *event_ptr); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + + void ReenableBreakpointSite (); +private: + + lldb::addr_t m_breakpoint_addr; + lldb::user_id_t m_breakpoint_site_id; + bool m_auto_continue; + bool m_reenabled_breakpoint_site; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverBreakpoint); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOverBreakpoint_h_ diff --git a/include/lldb/Target/ThreadPlanStepOverRange.h b/include/lldb/Target/ThreadPlanStepOverRange.h new file mode 100644 index 00000000000..de9e66829dc --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepOverRange.h @@ -0,0 +1,52 @@ +//===-- ThreadPlanStepOverRange.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepOverRange_h_ +#define liblldb_ThreadPlanStepOverRange_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlanStepRange.h" + +namespace lldb_private { + +class ThreadPlanStepOverRange : public ThreadPlanStepRange +{ +public: + + ThreadPlanStepOverRange (Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + virtual ~ThreadPlanStepOverRange (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ShouldStop (Event *event_ptr); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + +private: + + bool m_first_resume; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepOverRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepOverRange_h_ diff --git a/include/lldb/Target/ThreadPlanStepRange.h b/include/lldb/Target/ThreadPlanStepRange.h new file mode 100644 index 00000000000..486fd652839 --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepRange.h @@ -0,0 +1,94 @@ +//===-- ThreadPlanStepRange.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepRange_h_ +#define liblldb_ThreadPlanStepRange_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Target/StackID.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanShouldStopHere.h" + +namespace lldb_private { + +class ThreadPlanStepRange : public ThreadPlan +{ +public: + ThreadPlanStepRange (ThreadPlanKind kind, + const char *name, + Thread &thread, + const AddressRange &range, + const SymbolContext &addr_context, + lldb::RunMode stop_others); + + virtual ~ThreadPlanStepRange (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level) = 0; + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr) = 0; + virtual Vote ShouldReportStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void DidPush (); + virtual bool IsPlanStale (); + + + void AddRange(const AddressRange &new_range); + +protected: + + bool InRange(); + lldb::FrameComparison CompareCurrentFrameToStartFrame(); + bool InSymbol(); + void DumpRanges (Stream *s); + + Disassembler * + GetDisassembler (); + + InstructionList * + GetInstructionsForAddress(lldb::addr_t addr, size_t &range_index, size_t &insn_offset); + + // Pushes a plan to proceed through the next section of instructions in the range - usually just a RunToAddress + // plan to run to the next branch. Returns true if it pushed such a plan. If there was no available 'quick run' + // plan, then just single step. + bool + SetNextBranchBreakpoint (); + + void + ClearNextBranchBreakpoint(); + + bool + NextRangeBreakpointExplainsStop (lldb::StopInfoSP stop_info_sp); + + SymbolContext m_addr_context; + std::vector m_address_ranges; + lldb::RunMode m_stop_others; + StackID m_stack_id; // Use the stack ID so we can tell step out from step in. + bool m_no_more_plans; // Need this one so we can tell if we stepped into a call, + // but can't continue, in which case we are done. + bool m_first_run_event; // We want to broadcast only one running event, our first. + lldb::BreakpointSP m_next_branch_bp_sp; + bool m_use_fast_step; + +private: + std::vector m_instruction_ranges; + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepRange); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepRange_h_ diff --git a/include/lldb/Target/ThreadPlanStepThrough.h b/include/lldb/Target/ThreadPlanStepThrough.h new file mode 100644 index 00000000000..16979663eb1 --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepThrough.h @@ -0,0 +1,71 @@ +//===-- ThreadPlanStepThrough.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepThrough_h_ +#define liblldb_ThreadPlanStepThrough_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + +class ThreadPlanStepThrough : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepThrough (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + virtual void DidPush(); + +protected: + virtual bool DoPlanExplainsStop (Event *event_ptr); + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + + ThreadPlanStepThrough (Thread &thread, + StackID &return_stack_id, + bool stop_others); + + void + LookForPlanToStepThroughFromCurrentPC (); + + bool + HitOurBackstopBreakpoint(); + +private: + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepThrough (StackID &return_stack_id, + bool abort_other_plans, + bool stop_others); + + void ClearBackstopBreakpoint(); + + lldb::ThreadPlanSP m_sub_plan_sp; + lldb::addr_t m_start_address; + lldb::break_id_t m_backstop_bkpt_id; + lldb::addr_t m_backstop_addr; + StackID m_return_stack_id; + bool m_stop_others; + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepThrough); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepThrough_h_ diff --git a/include/lldb/Target/ThreadPlanStepUntil.h b/include/lldb/Target/ThreadPlanStepUntil.h new file mode 100644 index 00000000000..5aa3876df53 --- /dev/null +++ b/include/lldb/Target/ThreadPlanStepUntil.h @@ -0,0 +1,80 @@ +//===-- ThreadPlanStepUntil.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanStepUntil_h_ +#define liblldb_ThreadPlanStepUntil_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadPlan.h" + +namespace lldb_private { + + +class ThreadPlanStepUntil : public ThreadPlan +{ +public: + virtual ~ThreadPlanStepUntil (); + + virtual void GetDescription (Stream *s, lldb::DescriptionLevel level); + virtual bool ValidatePlan (Stream *error); + virtual bool ShouldStop (Event *event_ptr); + virtual bool StopOthers (); + virtual lldb::StateType GetPlanRunState (); + virtual bool WillStop (); + virtual bool MischiefManaged (); + +protected: + virtual bool DoWillResume (lldb::StateType resume_state, bool current_plan); + virtual bool DoPlanExplainsStop (Event *event_ptr); + + ThreadPlanStepUntil (Thread &thread, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others, + uint32_t frame_idx = 0); + void AnalyzeStop(void); + +private: + + StackID m_stack_id; + lldb::addr_t m_step_from_insn; + lldb::break_id_t m_return_bp_id; + lldb::addr_t m_return_addr; + bool m_stepped_out; + bool m_should_stop; + bool m_ran_analyze; + bool m_explains_stop; + + typedef std::map until_collection; + until_collection m_until_points; + bool m_stop_others; + + void Clear(); + + friend lldb::ThreadPlanSP + Thread::QueueThreadPlanForStepUntil (bool abort_other_plans, + lldb::addr_t *address_list, + size_t num_addresses, + bool stop_others, + uint32_t frame_idx); + + // Need an appropriate marker for the current stack so we can tell step out + // from step in. + + DISALLOW_COPY_AND_ASSIGN (ThreadPlanStepUntil); + +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanStepUntil_h_ diff --git a/include/lldb/Target/ThreadPlanTracer.h b/include/lldb/Target/ThreadPlanTracer.h new file mode 100644 index 00000000000..4eb0c783e57 --- /dev/null +++ b/include/lldb/Target/ThreadPlanTracer.h @@ -0,0 +1,131 @@ +//===-- ThreadPlanTracer.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadPlanTracer_h_ +#define liblldb_ThreadPlanTracer_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Symbol/TaggedASTType.h" +#include "lldb/Target/Thread.h" + +namespace lldb_private { + +class ThreadPlanTracer +{ +friend class ThreadPlan; + +public: + + typedef enum ThreadPlanTracerStyle + { + eLocation = 0, + eStateChange, + eCheckFrames, + ePython + } ThreadPlanTracerStyle; + ThreadPlanTracer (Thread &thread, lldb::StreamSP &stream_sp); + ThreadPlanTracer (Thread &thread); + + virtual ~ThreadPlanTracer() + { + } + + virtual void TracingStarted () + { + + } + + virtual void TracingEnded () + { + + } + + bool + EnableTracing(bool value) + { + bool old_value = m_enabled; + m_enabled = value; + if (old_value == false && value == true) + TracingStarted(); + else if (old_value == true && value == false) + TracingEnded(); + + return old_value; + } + + bool + TracingEnabled() + { + return m_enabled; + } + + bool + EnableSingleStep (bool value) + { + bool old_value = m_single_step; + m_single_step = value; + return old_value; + } + + bool + SingleStepEnabled () + { + return m_single_step; + } + +protected: + Thread &m_thread; + + Stream * + GetLogStream (); + + + + virtual void Log(); + +private: + bool + TracerExplainsStop (); + + bool m_single_step; + bool m_enabled; + lldb::StreamSP m_stream_sp; +}; + +class ThreadPlanAssemblyTracer : public ThreadPlanTracer +{ +public: + ThreadPlanAssemblyTracer (Thread &thread, lldb::StreamSP &stream_sp); + ThreadPlanAssemblyTracer (Thread &thread); + virtual ~ThreadPlanAssemblyTracer (); + virtual void TracingStarted (); + virtual void TracingEnded (); + virtual void Log(); +private: + + Disassembler * + GetDisassembler (); + + TypeFromUser + GetIntPointerType(); + + lldb::DisassemblerSP m_disassembler_sp; + TypeFromUser m_intptr_type; + std::vector m_register_values; + lldb::DataBufferSP m_buffer_sp; +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadPlanTracer_h_ diff --git a/include/lldb/Target/ThreadSpec.h b/include/lldb/Target/ThreadSpec.h new file mode 100644 index 00000000000..e0d30934f37 --- /dev/null +++ b/include/lldb/Target/ThreadSpec.h @@ -0,0 +1,155 @@ +//===-- ThreadSpec.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ThreadSpec_h_ +#define liblldb_ThreadSpec_h_ + +#include +#include + +#include "lldb/lldb-private.h" + +namespace lldb_private { + +// Note: For now the thread spec has only fixed elements - +// Thread ID +// Thread Index +// Thread Name +// Thread Queue Name +// +// But if we need more generality, we can hang a key/value map off of this structure. +// That's why the thread matches spec test is done as a virtual method in Thread::MatchesSpec, +// since it is the native thread that would know how to interpret the keys. +// I was going to do the Queue Name this way out of sheer orneriness, but that seems a +// sufficiently general concept, so I put it in here on its own. + +class ThreadSpec +{ +public: + ThreadSpec (); + + ThreadSpec (const ThreadSpec &rhs); + + const ThreadSpec & + operator=(const ThreadSpec &rhs); + + void + SetIndex (uint32_t index) + { + m_index = index; + } + + void + SetTID (lldb::tid_t tid) + { + m_tid = tid; + } + + void + SetName (const char *name) + { + m_name = name; + } + + void + SetQueueName (const char *queue_name) + { + m_queue_name = queue_name; + } + + uint32_t + GetIndex () const + { + return m_index; + } + + lldb::tid_t + GetTID () const + { + return m_tid; + } + + const char * + GetName () const; + + const char * + GetQueueName () const; + + bool + TIDMatches (lldb::tid_t thread_id) const + { + if (m_tid == LLDB_INVALID_THREAD_ID || thread_id == LLDB_INVALID_THREAD_ID) + return true; + else + return thread_id == m_tid; + } + + bool + TIDMatches (Thread &thread) const; + + bool + IndexMatches (uint32_t index) const + { + if (m_index == UINT32_MAX || index == UINT32_MAX) + return true; + else + return index == m_index; + } + + bool + IndexMatches (Thread &thread) const; + + bool + NameMatches (const char *name) const + { + if (m_name.empty()) + return true; + else if (name == NULL) + return false; + else + return m_name == name; + } + + bool + NameMatches (Thread &thread) const; + + bool + QueueNameMatches (const char *queue_name) const + { + if (m_queue_name.empty()) + return true; + else if (queue_name == NULL) + return false; + else + return m_queue_name == queue_name; + } + + bool + QueueNameMatches (Thread &thread) const; + + bool + ThreadPassesBasicTests (Thread &thread) const; + + bool + HasSpecification () const; + + void + GetDescription (Stream *s, lldb::DescriptionLevel level) const; + +protected: +private: + uint32_t m_index; + lldb::tid_t m_tid; + std::string m_name; + std::string m_queue_name; +}; + +} // namespace lldb_private + +#endif // liblldb_ThreadSpec_h_ diff --git a/include/lldb/Target/UnixSignals.h b/include/lldb/Target/UnixSignals.h new file mode 100644 index 00000000000..f47a90bbf54 --- /dev/null +++ b/include/lldb/Target/UnixSignals.h @@ -0,0 +1,144 @@ +//===-- UnixSignals.h -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_UnixSignals_h_ +#define lldb_UnixSignals_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/ConstString.h" + +namespace lldb_private +{ + +class UnixSignals +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + UnixSignals(); + + virtual + ~UnixSignals(); + + const char * + GetSignalAsCString (int32_t signo) const; + + bool + SignalIsValid (int32_t signo) const; + + int32_t + GetSignalNumberFromName (const char *name) const; + + const char * + GetSignalInfo (int32_t signo, + bool &should_suppress, + bool &should_stop, + bool &should_notify) const; + + bool + GetShouldSuppress (int32_t signo) const; + + bool + SetShouldSuppress (int32_t signo, + bool value); + + bool + SetShouldSuppress (const char *signal_name, + bool value); + + bool + GetShouldStop (int32_t signo) const; + + bool + SetShouldStop (int32_t signo, + bool value); + bool + SetShouldStop (const char *signal_name, + bool value); + + bool + GetShouldNotify (int32_t signo) const; + + bool + SetShouldNotify (int32_t signo, bool value); + + bool + SetShouldNotify (const char *signal_name, + bool value); + + // These provide an iterator through the signals available on this system. + // Call GetFirstSignalNumber to get the first entry, then iterate on GetNextSignalNumber + // till you get back LLDB_INVALID_SIGNAL_NUMBER. + int32_t + GetFirstSignalNumber () const; + + int32_t + GetNextSignalNumber (int32_t current_signal) const; + + // We assume that the elements of this object are constant once it is constructed, + // since a process should never need to add or remove symbols as it runs. So don't + // call these functions anywhere but the constructor of your subclass of UnixSignals or in + // your Process Plugin's GetUnixSignals method before you return the UnixSignal object. + + void + AddSignal (int signo, + const char *name, + const char *short_name, + bool default_suppress, + bool default_stop, + bool default_notify, + const char *description); + + void + RemoveSignal (int signo); + +protected: + //------------------------------------------------------------------ + // Classes that inherit from UnixSignals can see and modify these + //------------------------------------------------------------------ + + struct Signal + { + ConstString m_name; + ConstString m_short_name; + std::string m_description; + bool m_suppress:1, + m_stop:1, + m_notify:1; + + Signal (const char *name, + const char *short_name, + bool default_suppress, + bool default_stop, + bool default_notify, + const char *description); + + ~Signal () {} + }; + + void + Reset (); + + typedef std::map collection; + + collection m_signals; + + DISALLOW_COPY_AND_ASSIGN (UnixSignals); +}; + +} // Namespace lldb +#endif // lldb_UnixSignals_h_ diff --git a/include/lldb/Target/Unwind.h b/include/lldb/Target/Unwind.h new file mode 100644 index 00000000000..7cda4aeb2e1 --- /dev/null +++ b/include/lldb/Target/Unwind.h @@ -0,0 +1,120 @@ +//===-- Unwind.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Unwind_h_ +#define liblldb_Unwind_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +class Unwind +{ +protected: + //------------------------------------------------------------------ + // Classes that inherit from Unwind can see and modify these + //------------------------------------------------------------------ + Unwind(Thread &thread) : + m_thread (thread), + m_unwind_mutex() + { + } + +public: + virtual + ~Unwind() + { + } + + void + Clear() + { + Mutex::Locker locker(m_unwind_mutex); + DoClear(); + + } + + uint32_t + GetFrameCount() + { + Mutex::Locker locker(m_unwind_mutex); + return DoGetFrameCount(); + } + + uint32_t + GetFramesUpTo (uint32_t end_idx) + { + lldb::addr_t cfa; + lldb::addr_t pc; + uint32_t idx; + + for (idx = 0; idx < end_idx; idx++) + { + if (!DoGetFrameInfoAtIndex (idx, cfa, pc)) + { + break; + } + } + return idx; + } + + bool + GetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc) + { + Mutex::Locker locker(m_unwind_mutex); + return DoGetFrameInfoAtIndex (frame_idx, cfa, pc); + } + + lldb::RegisterContextSP + CreateRegisterContextForFrame (StackFrame *frame) + { + Mutex::Locker locker(m_unwind_mutex); + return DoCreateRegisterContextForFrame (frame); + } + + Thread & + GetThread() + { + return m_thread; + } + +protected: + //------------------------------------------------------------------ + // Classes that inherit from Unwind can see and modify these + //------------------------------------------------------------------ + virtual void + DoClear() = 0; + + virtual uint32_t + DoGetFrameCount() = 0; + + virtual bool + DoGetFrameInfoAtIndex (uint32_t frame_idx, + lldb::addr_t& cfa, + lldb::addr_t& pc) = 0; + + virtual lldb::RegisterContextSP + DoCreateRegisterContextForFrame (StackFrame *frame) = 0; + + Thread &m_thread; + Mutex m_unwind_mutex; +private: + DISALLOW_COPY_AND_ASSIGN (Unwind); +}; + +} // namespace lldb_private + +#endif // liblldb_Unwind_h_ diff --git a/include/lldb/Target/UnwindAssembly.h b/include/lldb/Target/UnwindAssembly.h new file mode 100644 index 00000000000..6a4ae0c30f2 --- /dev/null +++ b/include/lldb/Target/UnwindAssembly.h @@ -0,0 +1,58 @@ +//===-- UnwindAssembly.h --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_UnwindAssembly_h_ +#define utility_UnwindAssembly_h_ + +#include "lldb/lldb-private.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/PluginInterface.h" + +namespace lldb_private { + +class UnwindAssembly : + public PluginInterface +{ +public: + static UnwindAssembly* + FindPlugin (const ArchSpec &arch); + + virtual + ~UnwindAssembly(); + + virtual bool + GetNonCallSiteUnwindPlanFromAssembly (AddressRange& func, + Thread& thread, + UnwindPlan& unwind_plan) = 0; + + virtual bool + GetFastUnwindPlan (AddressRange& func, + Thread& thread, + UnwindPlan &unwind_plan) = 0; + + // thread may be NULL in which case we only use the Target (e.g. if this is called pre-process-launch). + virtual bool + FirstNonPrologueInsn (AddressRange& func, + const lldb_private::ExecutionContext &exe_ctx, + Address& first_non_prologue_insn) = 0; + +protected: + UnwindAssembly (const ArchSpec &arch); + ArchSpec m_arch; + +private: + UnwindAssembly(); // Outlaw default constructor + DISALLOW_COPY_AND_ASSIGN (UnwindAssembly); +}; + +} // namespace lldb_private + +#endif //utility_UnwindAssembly_h_ + + diff --git a/include/lldb/Utility/AnsiTerminal.h b/include/lldb/Utility/AnsiTerminal.h new file mode 100644 index 00000000000..036950c1bd4 --- /dev/null +++ b/include/lldb/Utility/AnsiTerminal.h @@ -0,0 +1,156 @@ +//===---------------------AnsiTerminal.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + + +#define ANSI_FG_COLOR_BLACK 30 +#define ANSI_FG_COLOR_RED 31 +#define ANSI_FG_COLOR_GREEN 32 +#define ANSI_FG_COLOR_YELLOW 33 +#define ANSI_FG_COLOR_BLUE 34 +#define ANSI_FG_COLOR_PURPLE 35 +#define ANSI_FG_COLOR_CYAN 36 +#define ANSI_FG_COLOR_WHITE 37 + +#define ANSI_BG_COLOR_BLACK 40 +#define ANSI_BG_COLOR_RED 41 +#define ANSI_BG_COLOR_GREEN 42 +#define ANSI_BG_COLOR_YELLOW 43 +#define ANSI_BG_COLOR_BLUE 44 +#define ANSI_BG_COLOR_PURPLE 45 +#define ANSI_BG_COLOR_CYAN 46 +#define ANSI_BG_COLOR_WHITE 47 + +#define ANSI_SPECIAL_FRAMED 51 +#define ANSI_SPECIAL_ENCIRCLED 52 + +#define ANSI_CTRL_NORMAL 0 +#define ANSI_CTRL_BOLD 1 +#define ANSI_CTRL_FAINT 2 +#define ANSI_CTRL_ITALIC 3 +#define ANSI_CTRL_UNDERLINE 4 +#define ANSI_CTRL_SLOW_BLINK 5 +#define ANSI_CTRL_FAST_BLINK 6 +#define ANSI_CTRL_IMAGE_NEGATIVE 7 +#define ANSI_CTRL_CONCEAL 8 +#define ANSI_CTRL_CROSSED_OUT 9 + +#define ANSI_ESC_START "\033[" +#define ANSI_ESC_END "m" + +#define ANSI_1_CTRL(ctrl1) "\033["##ctrl1 ANSI_ESC_END +#define ANSI_2_CTRL(ctrl1,ctrl2) "\033["##ctrl1";"##ctrl2 ANSI_ESC_END + +namespace lldb_utility { + + namespace ansi { + const char *k_escape_start = "\033["; + const char *k_escape_end = "m"; + + const char *k_fg_black = "30"; + const char *k_fg_red = "31"; + const char *k_fg_green = "32"; + const char *k_fg_yellow = "33"; + const char *k_fg_blue = "34"; + const char *k_fg_purple = "35"; + const char *k_fg_cyan = "36"; + const char *k_fg_white = "37"; + + const char *k_bg_black = "40"; + const char *k_bg_red = "41"; + const char *k_bg_green = "42"; + const char *k_bg_yellow = "43"; + const char *k_bg_blue = "44"; + const char *k_bg_purple = "45"; + const char *k_bg_cyan = "46"; + const char *k_bg_white = "47"; + + const char *k_ctrl_normal = "0"; + const char *k_ctrl_bold = "1"; + const char *k_ctrl_faint = "2"; + const char *k_ctrl_italic = "3"; + const char *k_ctrl_underline = "4"; + const char *k_ctrl_slow_blink = "5"; + const char *k_ctrl_fast_blink = "6"; + const char *k_ctrl_negative = "7"; + const char *k_ctrl_conceal = "8"; + const char *k_ctrl_crossed_out = "9"; + + inline std::string + FormatAnsiTerminalCodes(const char *format, bool do_color = true) + { + // Convert "${ansi.XXX}" tokens to ansi values or clear them if do_color is false. + static const struct + { + const char *name; + const char *value; + } g_color_tokens[] = + { + #define _TO_STR2(_val) #_val + #define _TO_STR(_val) _TO_STR2(_val) + { "fg.black}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END }, + { "fg.red}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END }, + { "fg.green}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END }, + { "fg.yellow}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END }, + { "fg.blue}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END }, + { "fg.purple}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END }, + { "fg.cyan}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END }, + { "fg.white}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END }, + { "bg.black}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END }, + { "bg.red}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END }, + { "bg.green}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END }, + { "bg.yellow}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END }, + { "bg.blue}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END }, + { "bg.purple}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END }, + { "bg.cyan}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END }, + { "bg.white}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END }, + { "normal}", ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END }, + { "bold}", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END }, + { "faint}", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END }, + { "italic}", ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END }, + { "underline}", ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END }, + { "slow-blink}", ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END }, + { "fast-blink}", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END }, + { "negative}", ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END }, + { "conceal}", ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END }, + { "crossed-out}", ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END }, + #undef _TO_STR + #undef _TO_STR2 + }; + static const char tok_hdr[] = "${ansi."; + + std::string fmt; + for (const char *p = format; *p; ++p) + { + const char *tok_start = strstr (p, tok_hdr); + if (!tok_start) + { + fmt.append (p, strlen(p)); + break; + } + + fmt.append (p, tok_start - p); + p = tok_start; + + const char *tok_str = tok_start + sizeof(tok_hdr) - 1; + for (size_t i = 0; i < sizeof(g_color_tokens) / sizeof(g_color_tokens[0]); ++i) + { + if (!strncmp (tok_str, g_color_tokens[i].name, strlen(g_color_tokens[i].name))) + { + if (do_color) + fmt.append (g_color_tokens[i].value); + p = tok_str + strlen (g_color_tokens[i].name) - 1; + break; + } + } + } + return fmt; + } + } +} diff --git a/include/lldb/Utility/CleanUp.h b/include/lldb/Utility/CleanUp.h new file mode 100644 index 00000000000..ab15d1999b7 --- /dev/null +++ b/include/lldb/Utility/CleanUp.h @@ -0,0 +1,322 @@ +//===-- CleanUp.h -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CleanUp_h_ +#define liblldb_CleanUp_h_ + +#include "lldb/lldb-public.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +// Templated class that guarantees that a cleanup callback function will +// be called. The cleanup function will be called once under the +// following conditions: +// - when the object goes out of scope +// - when the user explicitly calls clean. +// - the current value will be cleaned up when a new value is set using +// set(T value) as long as the current value hasn't already been cleaned. +// +// This class is designed to be used with simple types for type T (like +// file descriptors, opaque handles, pointers, etc). If more complex +// type T objects are desired, we need to probably specialize this class +// to take "const T&" for all input T parameters. Yet if a type T is +// complex already it might be better to build the cleanup funcionality +// into T. +// +// The cleanup function must take one argument that is of type T. +// The calback function return type is R. The return value is currently +// needed for "CallbackType". If there is an easy way to get around the +// need for the return value we can change this class. +// +// The two template parameters are: +// T - The variable type of value that will be stored and used as the +// sole argument for the cleanup callback. +// R - The return type for the cleanup function. +// +// EXAMPLES +// // Use with file handles that get opened where you want to close +// // them. Below we use "int open(const char *path, int oflag, ...)" +// // which returns an integer file descriptor. -1 is the invalid file +// // descriptor so to make an object that will call "int close(int fd)" +// // automatically we can use: +// +// CleanUp fd(open("/tmp/a.txt", O_RDONLY, 0), -1, close); +// +// // malloc/free example +// CleanUp malloced_bytes(malloc(32), NULL, free); +//---------------------------------------------------------------------- +template +class CleanUp +{ +public: + typedef T value_type; + typedef R (*CallbackType)(value_type); + + //---------------------------------------------------------------------- + // Constructor that sets the current value only. No values are + // considered to be invalid and the cleanup function will be called + // regardless of the value of m_current_value. + //---------------------------------------------------------------------- + CleanUp (value_type value, CallbackType callback) : + m_current_value (value), + m_invalid_value (), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (false) + { + } + + //---------------------------------------------------------------------- + // Constructor that sets the current value and also the invalid value. + // The cleanup function will be called on "m_value" as long as it isn't + // equal to "m_invalid_value". + //---------------------------------------------------------------------- + CleanUp (value_type value, value_type invalid, CallbackType callback) : + m_current_value (value), + m_invalid_value (invalid), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (true) + { + } + + //---------------------------------------------------------------------- + // Automatically cleanup when this object goes out of scope. + //---------------------------------------------------------------------- + ~CleanUp () + { + clean(); + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + value_type get() + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + const value_type + get() const + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Reset the owned value to "value". If a current value is valid and + // the cleanup callback hasn't been called, the previous value will + // be cleaned up (see void CleanUp::clean()). + //---------------------------------------------------------------------- + void + set (const value_type value) + { + // Cleanup the current value if needed + clean (); + // Now set the new value and mark our callback as not called + m_callback_called = false; + m_current_value = value; + } + + //---------------------------------------------------------------------- + // Checks is "m_current_value" is valid. The value is considered valid + // no invalid value was supplied during construction of this object or + // if an invalid value was supplied and "m_current_value" is not equal + // to "m_invalid_value". + // + // Returns true if "m_current_value" is valid, false otherwise. + //---------------------------------------------------------------------- + bool + is_valid() const + { + if (m_invalid_value_is_valid) + return m_current_value != m_invalid_value; + return true; + } + + //---------------------------------------------------------------------- + // This function will call the cleanup callback provided in the + // constructor one time if the value is considered valid (See is_valid()). + // This function sets m_callback_called to true so we don't call the + // cleanup callback multiple times on the same value. + //---------------------------------------------------------------------- + void + clean() + { + if (m_callback && !m_callback_called) + { + m_callback_called = true; + if (is_valid()) + m_callback(m_current_value); + } + } + + //---------------------------------------------------------------------- + // Cancels the cleanup that would have been called on "m_current_value" + // if it was valid. This function can be used to release the value + // contained in this object so ownership can be transfered to the caller. + //---------------------------------------------------------------------- + value_type + release () + { + m_callback_called = true; + return m_current_value; + } + +private: + value_type m_current_value; + const value_type m_invalid_value; + CallbackType m_callback; + bool m_callback_called; + bool m_invalid_value_is_valid; + + // Outlaw default constructor, copy constructor and the assignment operator + DISALLOW_COPY_AND_ASSIGN (CleanUp); +}; + +template +class CleanUp2 +{ +public: + typedef T value_type; + typedef R (*CallbackType)(value_type, A0); + + //---------------------------------------------------------------------- + // Constructor that sets the current value only. No values are + // considered to be invalid and the cleanup function will be called + // regardless of the value of m_current_value. + //---------------------------------------------------------------------- + CleanUp2 (value_type value, CallbackType callback, A0 arg) : + m_current_value (value), + m_invalid_value (), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (false), + m_argument(arg) + { + } + + //---------------------------------------------------------------------- + // Constructor that sets the current value and also the invalid value. + // The cleanup function will be called on "m_value" as long as it isn't + // equal to "m_invalid_value". + //---------------------------------------------------------------------- + CleanUp2 (value_type value, value_type invalid, CallbackType callback, A0 arg) : + m_current_value (value), + m_invalid_value (invalid), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (true), + m_argument(arg) + { + } + + //---------------------------------------------------------------------- + // Automatically cleanup when this object goes out of scope. + //---------------------------------------------------------------------- + ~CleanUp2 () + { + clean(); + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + value_type get() + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + const value_type + get() const + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Reset the owned value to "value". If a current value is valid and + // the cleanup callback hasn't been called, the previous value will + // be cleaned up (see void CleanUp::clean()). + //---------------------------------------------------------------------- + void + set (const value_type value) + { + // Cleanup the current value if needed + clean (); + // Now set the new value and mark our callback as not called + m_callback_called = false; + m_current_value = value; + } + + //---------------------------------------------------------------------- + // Checks is "m_current_value" is valid. The value is considered valid + // no invalid value was supplied during construction of this object or + // if an invalid value was supplied and "m_current_value" is not equal + // to "m_invalid_value". + // + // Returns true if "m_current_value" is valid, false otherwise. + //---------------------------------------------------------------------- + bool + is_valid() const + { + if (m_invalid_value_is_valid) + return m_current_value != m_invalid_value; + return true; + } + + //---------------------------------------------------------------------- + // This function will call the cleanup callback provided in the + // constructor one time if the value is considered valid (See is_valid()). + // This function sets m_callback_called to true so we don't call the + // cleanup callback multiple times on the same value. + //---------------------------------------------------------------------- + void + clean() + { + if (m_callback && !m_callback_called) + { + m_callback_called = true; + if (is_valid()) + m_callback(m_current_value, m_argument); + } + } + + //---------------------------------------------------------------------- + // Cancels the cleanup that would have been called on "m_current_value" + // if it was valid. This function can be used to release the value + // contained in this object so ownership can be transfered to the caller. + //---------------------------------------------------------------------- + value_type + release () + { + m_callback_called = true; + return m_current_value; + } + +private: + value_type m_current_value; + const value_type m_invalid_value; + CallbackType m_callback; + bool m_callback_called; + bool m_invalid_value_is_valid; + A0 m_argument; + + // Outlaw default constructor, copy constructor and the assignment operator + DISALLOW_COPY_AND_ASSIGN (CleanUp2); +}; + +} // namespace lldb_utility + +#endif // #ifndef liblldb_CleanUp_h_ diff --git a/include/lldb/Utility/PriorityPointerPair.h b/include/lldb/Utility/PriorityPointerPair.h new file mode 100644 index 00000000000..49f0765d0f9 --- /dev/null +++ b/include/lldb/Utility/PriorityPointerPair.h @@ -0,0 +1,150 @@ +//===-- PriorityPointerPair.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PriorityPointerPair_h_ +#define liblldb_PriorityPointerPair_h_ + +#include "lldb/lldb-public.h" +#include "lldb/Utility/SharingPtr.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +// A prioritized pair of SharedPtr. One of the two pointers is high +// priority, the other is low priority. +// The Get() method always returns high, if *high != NULL, +// otherwise, low is returned (even if *low == NULL) +//---------------------------------------------------------------------- + +template +class PriorityPointerPair +{ +public: + + typedef T& reference_type; + typedef T* pointer_type; + + typedef typename std::shared_ptr T_SP; + + PriorityPointerPair() : + m_high(), + m_low() + {} + + PriorityPointerPair(pointer_type high, + pointer_type low) : + m_high(high), + m_low(low) + {} + + PriorityPointerPair(pointer_type low) : + m_high(), + m_low(low) + {} + + PriorityPointerPair(T_SP& high, + T_SP& low) : + m_high(high), + m_low(low) + {} + + PriorityPointerPair(T_SP& low) : + m_high(), + m_low(low) + {} + + void + SwapLow(pointer_type l) + { + m_low.swap(l); + } + + void + SwapHigh(pointer_type h) + { + m_high.swap(h); + } + + void + SwapLow(T_SP l) + { + m_low.swap(l); + } + + void + SwapHigh(T_SP h) + { + m_high.swap(h); + } + + T_SP + GetLow() + { + return m_low; + } + + T_SP + GetHigh() + { + return m_high; + } + + T_SP + Get() + { + if (m_high.get()) + return m_high; + return m_low; + } + + void + ResetHigh() + { + m_high.reset(); + } + + void + ResetLow() + { + m_low.reset(); + } + + void + Reset() + { + ResetLow(); + ResetHigh(); + } + + reference_type + operator*() const + { + return Get().operator*(); + } + + pointer_type + operator->() const + { + return Get().operator->(); + } + + ~PriorityPointerPair(); + +private: + + T_SP m_high; + T_SP m_low; + + DISALLOW_COPY_AND_ASSIGN (PriorityPointerPair); + +}; + +} // namespace lldb_utility + +#endif // #ifndef liblldb_PriorityPointerPair_h_ diff --git a/include/lldb/Utility/PseudoTerminal.h b/include/lldb/Utility/PseudoTerminal.h new file mode 100644 index 00000000000..c79800fab75 --- /dev/null +++ b/include/lldb/Utility/PseudoTerminal.h @@ -0,0 +1,266 @@ +//===-- PseudoTerminal.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PseudoTerminal_h_ +#define liblldb_PseudoTerminal_h_ +#if defined(__cplusplus) + + +#include +#include + +#include "lldb/lldb-defines.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +/// @class PseudoTerminal PseudoTerminal.h "lldb/Core/PseudoTerminal.h" +/// @brief A pseudo terminal helper class. +/// +/// The pseudo terminal class abtracts the use of pseudo terminals on +/// the host system. +//---------------------------------------------------------------------- +class PseudoTerminal +{ +public: + enum + { + invalid_fd = -1 ///< Invalid file descriptor value + }; + + //------------------------------------------------------------------ + /// Default constructor + /// + /// Constructs this object with invalid master and slave file + /// descriptors. + //------------------------------------------------------------------ + PseudoTerminal (); + + //------------------------------------------------------------------ + /// Destructor + /// + /// The destructor will close the master and slave file descriptors + /// if they are valid and ownwership has not been released using + /// one of: + /// @li PseudoTerminal::ReleaseMasterFileDescriptor() + /// @li PseudoTerminal::ReleaseSaveFileDescriptor() + //------------------------------------------------------------------ + ~PseudoTerminal (); + + //------------------------------------------------------------------ + /// Close the master file descriptor if it is valid. + //------------------------------------------------------------------ + void + CloseMasterFileDescriptor (); + + //------------------------------------------------------------------ + /// Close the slave file descriptor if it is valid. + //------------------------------------------------------------------ + void + CloseSlaveFileDescriptor (); + + //------------------------------------------------------------------ + /// Fork a child process that uses pseudo terminals for its stdio. + /// + /// In the parent process, a call to this function results in a pid + /// being returned. If the pid is valid, the master file descriptor + /// can be used for 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. The file handles can be released + /// using either: + /// @li PseudoTerminal::ReleaseMasterFileDescriptor() + /// @li PseudoTerminal::ReleaseSaveFileDescriptor() + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// @li \b Parent process: a child process ID that is greater + /// than zero, or -1 if the fork fails. + /// @li \b Child process: zero. + //------------------------------------------------------------------ + lldb::pid_t + Fork (char *error_str, size_t error_len); + + //------------------------------------------------------------------ + /// The master file descriptor accessor. + /// + /// This object retains ownership of the master file descriptor when + /// this accessor is used. Users can call the member function + /// PseudoTerminal::ReleaseMasterFileDescriptor() if this + /// object should release ownership of the slave file descriptor. + /// + /// @return + /// The master file descriptor, or PseudoTerminal::invalid_fd + /// if the master file descriptor is not currently valid. + /// + /// @see PseudoTerminal::ReleaseMasterFileDescriptor() + //------------------------------------------------------------------ + int + GetMasterFileDescriptor () const; + + //------------------------------------------------------------------ + /// The slave file descriptor accessor. + /// + /// This object retains ownership of the slave file descriptor when + /// this accessor is used. Users can call the member function + /// PseudoTerminal::ReleaseSlaveFileDescriptor() if this + /// object should release ownership of the slave file descriptor. + /// + /// @return + /// The slave file descriptor, or PseudoTerminal::invalid_fd + /// if the slave file descriptor is not currently valid. + /// + /// @see PseudoTerminal::ReleaseSlaveFileDescriptor() + //------------------------------------------------------------------ + int + GetSlaveFileDescriptor () const; + + //------------------------------------------------------------------ + /// Get the name of the slave pseudo terminal. + /// + /// A master pseudo terminal should already be valid prior to + /// calling this function. + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// The name of the slave pseudo terminal as a NULL terminated + /// C. This string that comes from static memory, so a copy of + /// the string should be made as subsequent calls can change + /// this value. NULL is returned if this object doesn't have + /// a valid master pseudo terminal opened or if the call to + /// \c ptsname() fails. + /// + /// @see PseudoTerminal::OpenFirstAvailableMaster() + //------------------------------------------------------------------ + const char* + GetSlaveName (char *error_str, size_t error_len) const; + + //------------------------------------------------------------------ + /// Open the first available pseudo terminal. + /// + /// Opens the first available pseudo terminal with \a oflag as the + /// permissions. The opened master file descriptor is stored in this + /// object and can be accessed by calling the + /// PseudoTerminal::GetMasterFileDescriptor() accessor. Clients + /// can call the PseudoTerminal::ReleaseMasterFileDescriptor() + /// accessor function if they wish to use the master file descriptor + /// beyond the lifespan of this object. + /// + /// If this object still has a valid master file descriptor when its + /// destructor is called, it will close it. + /// + /// @param[in] oflag + /// Flags to use when calling \c posix_openpt(\a oflag). + /// A value of "O_RDWR|O_NOCTTY" is suggested. + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// @li \b true when the a master files descriptor is + /// successfully opened. + /// @li \b false if anything goes wrong. + /// + /// @see PseudoTerminal::GetMasterFileDescriptor() + /// @see PseudoTerminal::ReleaseMasterFileDescriptor() + //------------------------------------------------------------------ + bool + OpenFirstAvailableMaster (int oflag, char *error_str, size_t error_len); + + //------------------------------------------------------------------ + /// Open the slave for the current master pseudo terminal. + /// + /// A master pseudo terminal should already be valid prior to + /// calling this function. The opened slave file descriptor is + /// stored in this object and can be accessed by calling the + /// PseudoTerminal::GetSlaveFileDescriptor() accessor. Clients + /// can call the PseudoTerminal::ReleaseSlaveFileDescriptor() + /// accessor function if they wish to use the slave file descriptor + /// beyond the lifespan of this object. + /// + /// If this object still has a valid slave file descriptor when its + /// destructor is called, it will close it. + /// + /// @param[in] oflag + /// Flags to use when calling \c open(\a oflag). + /// + /// @param[out] error + /// An pointer to an error that can describe any errors that + /// occur. This can be NULL if no error status is desired. + /// + /// @return + /// @li \b true when the a master files descriptor is + /// successfully opened. + /// @li \b false if anything goes wrong. + /// + /// @see PseudoTerminal::OpenFirstAvailableMaster() + /// @see PseudoTerminal::GetSlaveFileDescriptor() + /// @see PseudoTerminal::ReleaseSlaveFileDescriptor() + //------------------------------------------------------------------ + bool + OpenSlave (int oflag, char *error_str, size_t error_len); + + //------------------------------------------------------------------ + /// Release the master file descriptor. + /// + /// Releases 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. + /// + /// @return + /// The master file descriptor, or PseudoTerminal::invalid_fd + /// if the mast file descriptor is not currently valid. + //------------------------------------------------------------------ + int + ReleaseMasterFileDescriptor (); + + //------------------------------------------------------------------ + /// Release the slave file descriptor. + /// + /// 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. + /// + /// @return + /// The slave file descriptor, or PseudoTerminal::invalid_fd + /// if the slave file descriptor is not currently valid. + //------------------------------------------------------------------ + int + ReleaseSlaveFileDescriptor (); + +protected: + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + int m_master_fd; ///< The file descriptor for the master. + int m_slave_fd; ///< The file descriptor for the slave. + +private: + DISALLOW_COPY_AND_ASSIGN (PseudoTerminal); + +}; + +} // namespace lldb + +#endif // #if defined(__cplusplus) +#endif // #ifndef liblldb_PseudoTerminal_h_ diff --git a/include/lldb/Utility/PythonPointer.h b/include/lldb/Utility/PythonPointer.h new file mode 100644 index 00000000000..f782f7f1313 --- /dev/null +++ b/include/lldb/Utility/PythonPointer.h @@ -0,0 +1,77 @@ +//===---------------------PythonPointer.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_PythonPointer_h_ +#define utility_PythonPointer_h_ + +#include + +#if defined (__APPLE__) +#include +#else +#include +#endif + +namespace lldb_private { + +template +class PythonPointer +{ +public: + typedef PyObject* element_type; +private: + element_type* ptr_; + bool my_ref; +public: + + PythonPointer(element_type p, bool steal_ref = false) : + ptr_(p), + my_ref(!steal_ref) + { + if (my_ref) + Py_INCREF(ptr_); + } + + PythonPointer(const PythonPointer& r, bool steal_ref = false) : + ptr_(r.ptr_), + my_ref(!steal_ref) + { + if (my_ref) + Py_INCREF(ptr_); + } + + ~PythonPointer() + { + if (my_ref) + Py_XDECREF(ptr_); + } + + PythonPointer + StealReference() + { + return PythonPointer(ptr_,true); + } + + PythonPointer + DuplicateReference() + { + return PythonPointer(ptr_, false); + } + + element_type get() const {return ptr_;} + + bool IsNull() { return ptr_ == NULL; } + bool IsNone() { return ptr_ == Py_None; } + + operator PyObject* () { return ptr_; } +}; + +} // namespace lldb + +#endif // utility_PythonPointer_h_ diff --git a/include/lldb/Utility/Range.h b/include/lldb/Utility/Range.h new file mode 100644 index 00000000000..1257adb719a --- /dev/null +++ b/include/lldb/Utility/Range.h @@ -0,0 +1,89 @@ +//===--------------------- Range.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_Range_h_ +#define utility_Range_h_ + +#include +#include + +namespace lldb_utility { + +class Range +{ +public: + + typedef uint64_t ValueType; + + static const ValueType OPEN_END = UINT64_MAX; + + Range (const Range& rng); + + Range (ValueType low = 0, + ValueType high = OPEN_END); + + Range& + operator = (const Range& rhs); + + ValueType + GetLow () + { + return m_low; + } + + ValueType + GetHigh () + { + return m_high; + } + + void + SetLow (ValueType low) + { + m_low = low; + } + + void + SetHigh (ValueType high) + { + m_high = high; + } + + void + Flip (); + + void + Intersection (const Range& other); + + void + Union (const Range& other); + + typedef bool (*RangeCallback)(ValueType index); + + void + Iterate (RangeCallback callback); + + ValueType + GetSize (); + + bool + IsEmpty (); + +private: + + void + InitRange (); + + ValueType m_low; + ValueType m_high; +}; + +} // namespace lldb_private + +#endif // #ifndef utility_Range_h_ diff --git a/include/lldb/Utility/RefCounter.h b/include/lldb/Utility/RefCounter.h new file mode 100644 index 00000000000..6daed5498eb --- /dev/null +++ b/include/lldb/Utility/RefCounter.h @@ -0,0 +1,56 @@ +//===-- RefCounter.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_RefCounter_h_ +#define liblldb_RefCounter_h_ + +#include "lldb/lldb-public.h" + +namespace lldb_utility { + +//---------------------------------------------------------------------- +// A simple reference counter object. You need an uint32_t* to use it +// Once that is in place, everyone who needs to ref-count, can say +// RefCounter ref(ptr); +// (of course, the pointer is a shared resource, and must be accessible to +// everyone who needs it). Synchronization is handled by RefCounter itself +// The counter is decreased each time a RefCounter to it goes out of scope +//---------------------------------------------------------------------- +class RefCounter +{ +public: + typedef uint32_t value_type; + + RefCounter(value_type* ctr); + + ~RefCounter(); + +private: + value_type* m_counter; + DISALLOW_COPY_AND_ASSIGN (RefCounter); + + template + inline T + increment(T* t) + { + return __sync_fetch_and_add(t, 1); + } + + template + inline T + decrement(T* t) + { + return __sync_fetch_and_add(t, -1); + } + +}; + +} // namespace lldb_utility + +#endif // #ifndef liblldb_RefCounter_h_ diff --git a/include/lldb/Utility/SharedCluster.h b/include/lldb/Utility/SharedCluster.h new file mode 100644 index 00000000000..991af4b4fa4 --- /dev/null +++ b/include/lldb/Utility/SharedCluster.h @@ -0,0 +1,108 @@ +//===------------------SharedCluster.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_SharedCluster_h_ +#define utility_SharedCluster_h_ + +#include "lldb/Utility/SharingPtr.h" +#include "lldb/Host/Mutex.h" + +namespace lldb_private { + +namespace imp +{ + template + class shared_ptr_refcount : public lldb_private::imp::shared_count + { + public: + template shared_ptr_refcount (Y *in) : shared_count (0), manager(in) {} + + shared_ptr_refcount() : shared_count (0) {} + + virtual ~shared_ptr_refcount () + { + } + + virtual void on_zero_shared () + { + manager->DecrementRefCount(); + } + private: + T *manager; + }; + +} // namespace imp + +template +class ClusterManager +{ +public: + ClusterManager () : + m_objects(), + m_external_ref(0), + m_mutex(Mutex::eMutexTypeNormal) {} + + ~ClusterManager () + { + size_t n_items = m_objects.size(); + for (size_t i = 0; i < n_items; i++) + { + delete m_objects[i]; + } + // Decrement refcount should have been called on this ClusterManager, + // and it should have locked the mutex, now we will unlock it before + // we destroy it... + m_mutex.Unlock(); + } + + void ManageObject (T *new_object) + { + Mutex::Locker locker (m_mutex); + if (!ContainsObject(new_object)) + m_objects.push_back (new_object); + } + + typename lldb_private::SharingPtr GetSharedPointer(T *desired_object) + { + { + Mutex::Locker locker (m_mutex); + m_external_ref++; + assert (ContainsObject(desired_object)); + } + return typename lldb_private::SharingPtr (desired_object, new imp::shared_ptr_refcount (this)); + } + +private: + + bool ContainsObject (const T *desired_object) + { + typename std::vector::iterator pos, end = m_objects.end(); + pos = std::find(m_objects.begin(), end, desired_object); + return pos != end; + } + + void DecrementRefCount () + { + m_mutex.Lock(); + m_external_ref--; + if (m_external_ref == 0) + delete this; + else + m_mutex.Unlock(); + } + + friend class imp::shared_ptr_refcount; + + std::vector m_objects; + int m_external_ref; + Mutex m_mutex; +}; + +} // namespace lldb_private +#endif // utility_SharedCluster_h_ diff --git a/include/lldb/Utility/SharingPtr.h b/include/lldb/Utility/SharingPtr.h new file mode 100644 index 00000000000..814b54b5cfe --- /dev/null +++ b/include/lldb/Utility/SharingPtr.h @@ -0,0 +1,816 @@ +//===---------------------SharingPtr.h --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_SharingPtr_h_ +#define utility_SharingPtr_h_ + +#include +#include + +//#define ENABLE_SP_LOGGING 1 // DON'T CHECK THIS LINE IN UNLESS COMMENTED OUT +#if defined (ENABLE_SP_LOGGING) + +extern "C" void track_sp (void *sp_this, void *ptr, long count); + +#endif + +namespace lldb_private { + +namespace imp { + +template +inline T +increment(T& t) +{ + return __sync_add_and_fetch(&t, 1); +} + +template +inline T +decrement(T& t) +{ + return __sync_add_and_fetch(&t, -1); +} + +class shared_count +{ + shared_count(const shared_count&); + shared_count& operator=(const shared_count&); + +protected: + long shared_owners_; + virtual ~shared_count(); +private: + virtual void on_zero_shared() = 0; + +public: + explicit shared_count(long refs = 0) + : shared_owners_(refs) {} + + void add_shared(); + void release_shared(); + long use_count() const {return shared_owners_ + 1;} +}; + +template +class shared_ptr_pointer + : public shared_count +{ + T data_; +public: + shared_ptr_pointer(T p) + : data_(p) {} + +private: + virtual void on_zero_shared(); + + // Outlaw copy constructor and assignment operator to keep effictive C++ + // warnings down to a minumum + shared_ptr_pointer (const shared_ptr_pointer &); + shared_ptr_pointer & operator=(const shared_ptr_pointer &); +}; + +template +void +shared_ptr_pointer::on_zero_shared() +{ + delete data_; +} + +template +class shared_ptr_emplace + : public shared_count +{ + T data_; +public: + + shared_ptr_emplace() + : data_() {} + + template + shared_ptr_emplace(A0& a0) + : data_(a0) {} + + template + shared_ptr_emplace(A0& a0, A1& a1) + : data_(a0, a1) {} + + template + shared_ptr_emplace(A0& a0, A1& a1, A2& a2) + : data_(a0, a1, a2) {} + + template + shared_ptr_emplace(A0& a0, A1& a1, A2& a2, A3& a3) + : data_(a0, a1, a2, a3) {} + + template + shared_ptr_emplace(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) + : data_(a0, a1, a2, a3, a4) {} + +private: + virtual void on_zero_shared(); +public: + T* get() {return &data_;} +}; + +template +void +shared_ptr_emplace::on_zero_shared() +{ +} + +} // namespace + +template +class SharingPtr +{ +public: + typedef T element_type; +private: + element_type* ptr_; + imp::shared_count* cntrl_; + + struct nat {int for_bool_;}; +public: + SharingPtr(); + template explicit SharingPtr(Y* p); + template explicit SharingPtr(Y* p, imp::shared_count *ctrl_block); + template SharingPtr(const SharingPtr& r, element_type *p); + SharingPtr(const SharingPtr& r); + template + SharingPtr(const SharingPtr& r); + + ~SharingPtr(); + + SharingPtr& operator=(const SharingPtr& r); + template SharingPtr& operator=(const SharingPtr& r); + + void swap(SharingPtr& r); + void reset(); + template void reset(Y* p); + + element_type* get() const {return ptr_;} + element_type& operator*() const {return *ptr_;} + element_type* operator->() const {return ptr_;} + long use_count() const {return cntrl_ ? cntrl_->use_count() : 0;} + bool unique() const {return use_count() == 1;} + bool empty() const {return cntrl_ == 0;} + operator nat*() const {return (nat*)get();} + + static SharingPtr make_shared(); + + template + static SharingPtr make_shared(A0&); + + template + static SharingPtr make_shared(A0&, A1&); + + template + static SharingPtr make_shared(A0&, A1&, A2&); + + template + static SharingPtr make_shared(A0&, A1&, A2&, A3&); + + template + static SharingPtr make_shared(A0&, A1&, A2&, A3&, A4&); + +private: + + template friend class SharingPtr; +}; + +template +inline +SharingPtr::SharingPtr() + : ptr_(0), + cntrl_(0) +{ +} + +template +template +SharingPtr::SharingPtr(Y* p) + : ptr_(p), cntrl_(0) +{ + std::unique_ptr hold(p); + typedef imp::shared_ptr_pointer _CntrlBlk; + cntrl_ = new _CntrlBlk(p); + hold.release(); +} + +template +template +SharingPtr::SharingPtr(Y* p, imp::shared_count *cntrl_block) + : ptr_(p), cntrl_(cntrl_block) +{ +} + +template +template +inline +SharingPtr::SharingPtr(const SharingPtr& r, element_type *p) + : ptr_(p), + cntrl_(r.cntrl_) +{ + if (cntrl_) + cntrl_->add_shared(); +} + +template +inline +SharingPtr::SharingPtr(const SharingPtr& r) + : ptr_(r.ptr_), + cntrl_(r.cntrl_) +{ + if (cntrl_) + cntrl_->add_shared(); +} + +template +template +inline +SharingPtr::SharingPtr(const SharingPtr& r) + : ptr_(r.ptr_), + cntrl_(r.cntrl_) +{ + if (cntrl_) + cntrl_->add_shared(); +} + +template +SharingPtr::~SharingPtr() +{ + if (cntrl_) + cntrl_->release_shared(); +} + +template +inline +SharingPtr& +SharingPtr::operator=(const SharingPtr& r) +{ + SharingPtr(r).swap(*this); + return *this; +} + +template +template +inline +SharingPtr& +SharingPtr::operator=(const SharingPtr& r) +{ + SharingPtr(r).swap(*this); + return *this; +} + +template +inline +void +SharingPtr::swap(SharingPtr& r) +{ + std::swap(ptr_, r.ptr_); + std::swap(cntrl_, r.cntrl_); +} + +template +inline +void +SharingPtr::reset() +{ + SharingPtr().swap(*this); +} + +template +template +inline +void +SharingPtr::reset(Y* p) +{ + SharingPtr(p).swap(*this); +} + +template +SharingPtr +SharingPtr::make_shared() +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1, A2& a2) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1, a2); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1, A2& a2, A3& a3) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1, a2, a3); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +template +SharingPtr +SharingPtr::make_shared(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) +{ + typedef imp::shared_ptr_emplace CntrlBlk; + SharingPtr r; + r.cntrl_ = new CntrlBlk(a0, a1, a2, a3, a4); + r.ptr_ = static_cast(r.cntrl_)->get(); + return r; +} + +template +inline +SharingPtr +make_shared() +{ + return SharingPtr::make_shared(); +} + +template +inline +SharingPtr +make_shared(A0& a0) +{ + return SharingPtr::make_shared(a0); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1) +{ + return SharingPtr::make_shared(a0, a1); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1, A2& a2) +{ + return SharingPtr::make_shared(a0, a1, a2); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1, A2& a2, A3& a3) +{ + return SharingPtr::make_shared(a0, a1, a2, a3); +} + +template +inline +SharingPtr +make_shared(A0& a0, A1& a1, A2& a2, A3& a3, A4& a4) +{ + return SharingPtr::make_shared(a0, a1, a2, a3, a4); +} + + +template +inline +bool +operator==(const SharingPtr& __x, const SharingPtr& __y) +{ + return __x.get() == __y.get(); +} + +template +inline +bool +operator!=(const SharingPtr& __x, const SharingPtr& __y) +{ + return !(__x == __y); +} + +template +inline +bool +operator<(const SharingPtr& __x, const SharingPtr& __y) +{ + return __x.get() < __y.get(); +} + +template +inline +void +swap(SharingPtr& __x, SharingPtr& __y) +{ + __x.swap(__y); +} + +template +inline +SharingPtr +static_pointer_cast(const SharingPtr& r) +{ + return SharingPtr(r, static_cast(r.get())); +} + +template +SharingPtr +const_pointer_cast(const SharingPtr& r) +{ + return SharingPtr(r, const_cast(r.get())); +} + +template +class LoggingSharingPtr + : public SharingPtr +{ + typedef SharingPtr base; + +public: + typedef void (*Callback)(void*, const LoggingSharingPtr&, bool action); + // action: false means increment just happened + // true means decrement is about to happen + +private: + Callback cb_; + void* baton_; + +public: + LoggingSharingPtr() : cb_(0), baton_(0) {} + LoggingSharingPtr(Callback cb, void* baton) + : cb_(cb), baton_(baton) + { + if (cb_) + cb_(baton_, *this, false); + } + + template + LoggingSharingPtr(Y* p) + : base(p), cb_(0), baton_(0) {} + + template + LoggingSharingPtr(Y* p, Callback cb, void* baton) + : base(p), cb_(cb), baton_(baton) + { + if (cb_) + cb_(baton_, *this, false); + } + + ~LoggingSharingPtr() + { + if (cb_) + cb_(baton_, *this, true); + } + + LoggingSharingPtr(const LoggingSharingPtr& p) + : base(p), cb_(p.cb_), baton_(p.baton_) + { + if (cb_) + cb_(baton_, *this, false); + } + + LoggingSharingPtr& operator=(const LoggingSharingPtr& p) + { + if (cb_) + cb_(baton_, *this, true); + base::operator=(p); + cb_ = p.cb_; + baton_ = p.baton_; + if (cb_) + cb_(baton_, *this, false); + return *this; + } + + void reset() + { + if (cb_) + cb_(baton_, *this, true); + base::reset(); + } + + template + void reset(Y* p) + { + if (cb_) + cb_(baton_, *this, true); + base::reset(p); + if (cb_) + cb_(baton_, *this, false); + } + + void SetCallback(Callback cb, void* baton) + { + cb_ = cb; + baton_ = baton; + } + + void ClearCallback() + { + cb_ = 0; + baton_ = 0; + } +}; + + +template +class IntrusiveSharingPtr; + +template +class ReferenceCountedBase +{ +public: + explicit ReferenceCountedBase() + : shared_owners_(-1) + { + } + + void + add_shared(); + + void + release_shared(); + + long + use_count() const + { + return shared_owners_ + 1; + } + +protected: + long shared_owners_; + + friend class IntrusiveSharingPtr; + +private: + ReferenceCountedBase(const ReferenceCountedBase&); + ReferenceCountedBase& operator=(const ReferenceCountedBase&); +}; + + template + void + lldb_private::ReferenceCountedBase::add_shared() + { + imp::increment(shared_owners_); + } + + template + void + lldb_private::ReferenceCountedBase::release_shared() + { + if (imp::decrement(shared_owners_) == -1) + delete static_cast(this); + } + + +template +class ReferenceCountedBaseVirtual : public imp::shared_count +{ +public: + explicit ReferenceCountedBaseVirtual () : + imp::shared_count(-1) + { + } + + virtual + ~ReferenceCountedBaseVirtual () + { + } + + virtual void on_zero_shared (); + +}; + +template +void +ReferenceCountedBaseVirtual::on_zero_shared() +{ +} + +template +class IntrusiveSharingPtr +{ +public: + typedef T element_type; + + explicit + IntrusiveSharingPtr () : + ptr_(0) + { + } + + explicit + IntrusiveSharingPtr (T* ptr) : + ptr_(ptr) + { + add_shared(); + } + + IntrusiveSharingPtr (const IntrusiveSharingPtr& rhs) : + ptr_(rhs.ptr_) + { + add_shared(); + } + + template + IntrusiveSharingPtr (const IntrusiveSharingPtr& rhs) + : ptr_(rhs.get()) + { + add_shared(); + } + + IntrusiveSharingPtr& + operator= (const IntrusiveSharingPtr& rhs) + { + reset(rhs.get()); + return *this; + } + + template IntrusiveSharingPtr& + operator= (const IntrusiveSharingPtr& rhs) + { + reset(rhs.get()); + return *this; + } + + IntrusiveSharingPtr& + operator= (T *ptr) + { + reset(ptr); + return *this; + } + + ~IntrusiveSharingPtr() + { + release_shared(); +#if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE) + // NULL out the pointer in objects which can help with leaks detection. + // We don't enable this for LLDB_CONFIGURATION_BUILD_AND_INTEGRATION or + // when none of the LLDB_CONFIGURATION_XXX macros are defined since + // those would be builds for release. But for debug and release builds + // that are for development, we NULL out the pointers to catch potential + // issues. + ptr_ = NULL; +#endif // #if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE) + } + + T& + operator*() const + { + return *ptr_; + } + + T* + operator->() const + { + return ptr_; + } + + T* + get() const + { + return ptr_; + } + + operator bool() const + { + return ptr_ != 0; + } + + void + swap (IntrusiveSharingPtr& rhs) + { + std::swap(ptr_, rhs.ptr_); +#if defined (ENABLE_SP_LOGGING) + track_sp (this, ptr_, use_count()); + track_sp (&rhs, rhs.ptr_, rhs.use_count()); +#endif + } + + void + reset(T* ptr = NULL) + { + IntrusiveSharingPtr(ptr).swap(*this); + } + + long + use_count () const + { + if (ptr_) + return ptr_->use_count(); + return 0; + } + + bool + unique () const + { + return use_count () == 1; + } + +private: + element_type *ptr_; + + void + add_shared() + { + if (ptr_) + { + ptr_->add_shared(); +#if defined (ENABLE_SP_LOGGING) + track_sp (this, ptr_, ptr_->use_count()); +#endif + } + } + void + release_shared() + { + if (ptr_) + { +#if defined (ENABLE_SP_LOGGING) + track_sp (this, NULL, ptr_->use_count() - 1); +#endif + ptr_->release_shared(); + } + } +}; + +template +inline bool operator== (const IntrusiveSharingPtr& lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs.get() == rhs.get(); +} + +template +inline bool operator!= (const IntrusiveSharingPtr& lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs.get() != rhs.get(); +} + +template +inline bool operator== (const IntrusiveSharingPtr& lhs, U* rhs) +{ + return lhs.get() == rhs; +} + +template +inline bool operator!= (const IntrusiveSharingPtr& lhs, U* rhs) +{ + return lhs.get() != rhs; +} + +template +inline bool operator== (T* lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs == rhs.get(); +} + +template +inline bool operator!= (T* lhs, const IntrusiveSharingPtr& rhs) +{ + return lhs != rhs.get(); +} + +} // namespace lldb_private + +#endif // utility_SharingPtr_h_ diff --git a/include/lldb/Utility/Utils.h b/include/lldb/Utility/Utils.h new file mode 100644 index 00000000000..46bc1847c0b --- /dev/null +++ b/include/lldb/Utility/Utils.h @@ -0,0 +1,22 @@ +//===-- Utils.h -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef utility_Utils_h_ +#define utility_Utils_h_ + +// These utilities have llvm namespace. +#include "llvm/ADT/STLExtras.h" + +namespace lldb_private { + +// Add lldb utilities here. + +} // namespace lldb_private + +#endif // utility_Utils diff --git a/include/lldb/lldb-defines.h b/include/lldb/lldb-defines.h new file mode 100644 index 00000000000..3318aa15f5a --- /dev/null +++ b/include/lldb/lldb-defines.h @@ -0,0 +1,125 @@ +//===-- lldb-defines.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_defines_h_ +#define LLDB_lldb_defines_h_ + +#include "lldb/lldb-types.h" + +#if !defined(UINT32_MAX) + #define UINT32_MAX 4294967295U +#endif + +#if !defined(UINT64_MAX) + #define UINT64_MAX 18446744073709551615ULL +#endif + +//---------------------------------------------------------------------- +// LLDB version +// +// A build script phase can modify this version number if needed. +//---------------------------------------------------------------------- +//#define LLDB_VERSION +//#define LLDB_REVISION +//#define LLDB_VERSION_STRING + +//---------------------------------------------------------------------- +// LLDB defines +//---------------------------------------------------------------------- +#define LLDB_GENERIC_ERROR UINT32_MAX + +//---------------------------------------------------------------------- +// Breakpoints +//---------------------------------------------------------------------- +#define LLDB_INVALID_BREAK_ID 0 +#define LLDB_DEFAULT_BREAK_SIZE 0 +#define LLDB_BREAK_ID_IS_VALID(bid) ((bid) != (LLDB_INVALID_BREAK_ID)) +#define LLDB_BREAK_ID_IS_INTERNAL(bid) ((bid) < 0) + +//---------------------------------------------------------------------- +// Watchpoints +//---------------------------------------------------------------------- +#define LLDB_INVALID_WATCH_ID 0 +#define LLDB_WATCH_ID_IS_VALID(uid) ((uid) != (LLDB_INVALID_WATCH_ID)) +#define LLDB_WATCH_TYPE_READ (1u << 0) +#define LLDB_WATCH_TYPE_WRITE (1u << 1) +#define LLDB_WATCH_TYPE_IS_VALID(type) ((type | LLDB_WATCH_TYPE_READ) || (type | LLDB_WATCH_TYPE_WRITE)) + +//---------------------------------------------------------------------- +// Generic Register Numbers +//---------------------------------------------------------------------- +#define LLDB_REGNUM_GENERIC_PC 0 // Program Counter +#define LLDB_REGNUM_GENERIC_SP 1 // Stack Pointer +#define LLDB_REGNUM_GENERIC_FP 2 // Frame Pointer +#define LLDB_REGNUM_GENERIC_RA 3 // Return Address +#define LLDB_REGNUM_GENERIC_FLAGS 4 // Processor flags register +#define LLDB_REGNUM_GENERIC_ARG1 5 // The register that would contain pointer size or less argument 1 (if any) +#define LLDB_REGNUM_GENERIC_ARG2 6 // The register that would contain pointer size or less argument 2 (if any) +#define LLDB_REGNUM_GENERIC_ARG3 7 // The register that would contain pointer size or less argument 3 (if any) +#define LLDB_REGNUM_GENERIC_ARG4 8 // The register that would contain pointer size or less argument 4 (if any) +#define LLDB_REGNUM_GENERIC_ARG5 9 // The register that would contain pointer size or less argument 5 (if any) +#define LLDB_REGNUM_GENERIC_ARG6 10 // The register that would contain pointer size or less argument 6 (if any) +#define LLDB_REGNUM_GENERIC_ARG7 11 // The register that would contain pointer size or less argument 7 (if any) +#define LLDB_REGNUM_GENERIC_ARG8 12 // The register that would contain pointer size or less argument 8 (if any) +//--------------------------------------------------------------------- +/// Invalid value definitions +//---------------------------------------------------------------------- +#define LLDB_INVALID_ADDRESS UINT64_MAX +#define LLDB_INVALID_INDEX32 UINT32_MAX +#define LLDB_INVALID_IVAR_OFFSET UINT32_MAX +#define LLDB_INVALID_IMAGE_TOKEN UINT32_MAX +#define LLDB_INVALID_REGNUM UINT32_MAX +#define LLDB_INVALID_UID UINT64_MAX +#define LLDB_INVALID_PROCESS_ID 0 +#define LLDB_INVALID_THREAD_ID 0 +#define LLDB_INVALID_FRAME_ID UINT32_MAX +#define LLDB_INVALID_SIGNAL_NUMBER INT32_MAX +#define LLDB_INVALID_OFFSET UINT64_MAX // Must match max of lldb::offset_t + +//---------------------------------------------------------------------- +/// CPU Type defintions +//---------------------------------------------------------------------- +#define LLDB_ARCH_DEFAULT "systemArch" +#define LLDB_ARCH_DEFAULT_32BIT "systemArch32" +#define LLDB_ARCH_DEFAULT_64BIT "systemArch64" +#define LLDB_INVALID_CPUTYPE (0xFFFFFFFEu) + +//---------------------------------------------------------------------- +/// Option Set defintions +//---------------------------------------------------------------------- +// FIXME: I'm sure there's some #define magic that can create all 32 sets on the +// fly. That would have the added benefit of making this unreadable. +#define LLDB_MAX_NUM_OPTION_SETS 32 +#define LLDB_OPT_SET_ALL 0xFFFFFFFFU +#define LLDB_OPT_SET_1 (1U << 0) +#define LLDB_OPT_SET_2 (1U << 1) +#define LLDB_OPT_SET_3 (1U << 2) +#define LLDB_OPT_SET_4 (1U << 3) +#define LLDB_OPT_SET_5 (1U << 4) +#define LLDB_OPT_SET_6 (1U << 5) +#define LLDB_OPT_SET_7 (1U << 6) +#define LLDB_OPT_SET_8 (1U << 7) +#define LLDB_OPT_SET_9 (1U << 8) +#define LLDB_OPT_SET_10 (1U << 9) +#define LLDB_OPT_SET_FROM_TO(A, B) (((1U << (B)) - 1) ^ (((1U << (A))-1) >> 1)) + +#if defined(__cplusplus) + +//---------------------------------------------------------------------- +/// @def DISALLOW_COPY_AND_ASSIGN(TypeName) +/// Macro definition for easily disallowing copy constructor and +/// assignment operators in C++ classes. +//---------------------------------------------------------------------- +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + const TypeName& operator=(const TypeName&) + +#endif // #if defined(__cplusplus) + +#endif // LLDB_lldb_defines_h_ diff --git a/include/lldb/lldb-enumerations.h b/include/lldb/lldb-enumerations.h new file mode 100644 index 00000000000..6ec5ed6a35e --- /dev/null +++ b/include/lldb/lldb-enumerations.h @@ -0,0 +1,685 @@ +//===-- lldb-enumerations.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_enumerations_h_ +#define LLDB_lldb_enumerations_h_ + +namespace lldb { + + //---------------------------------------------------------------------- + // Process and Thread States + //---------------------------------------------------------------------- + typedef enum StateType + { + eStateInvalid = 0, + eStateUnloaded, ///< Process is object is valid, but not currently loaded + eStateConnected, ///< Process is connected to remote debug services, but not launched or attached to anything yet + eStateAttaching, ///< Process is currently trying to attach + eStateLaunching, ///< Process is in the process of launching + eStateStopped, ///< Process or thread is stopped and can be examined. + eStateRunning, ///< Process or thread is running and can't be examined. + eStateStepping, ///< Process or thread is in the process of stepping and can not be examined. + eStateCrashed, ///< Process or thread has crashed and can be examined. + eStateDetached, ///< Process has been detached and can't be examined. + eStateExited, ///< Process has exited and can't be examined. + eStateSuspended ///< Process or thread is in a suspended state as far + ///< as the debugger is concerned while other processes + ///< or threads get the chance to run. + } StateType; + + //---------------------------------------------------------------------- + // Launch Flags + //---------------------------------------------------------------------- + typedef enum LaunchFlags + { + eLaunchFlagNone = 0u, + eLaunchFlagExec = (1u << 0), ///< Exec when launching and turn the calling process into a new process + eLaunchFlagDebug = (1u << 1), ///< Stop as soon as the process launches to allow the process to be debugged + eLaunchFlagStopAtEntry = (1u << 2), ///< Stop at the program entry point instead of auto-continuing when launching or attaching at entry point + eLaunchFlagDisableASLR = (1u << 3), ///< Disable Address Space Layout Randomization + eLaunchFlagDisableSTDIO = (1u << 4), ///< Disable stdio for inferior process (e.g. for a GUI app) + eLaunchFlagLaunchInTTY = (1u << 5), ///< Launch the process in a new TTY if supported by the host + eLaunchFlagLaunchInShell= (1u << 6), ///< Launch the process inside a shell to get shell expansion + eLaunchFlagLaunchInSeparateProcessGroup = (1u << 7) ///< Launch the process in a separate process group + } LaunchFlags; + + //---------------------------------------------------------------------- + // Thread Run Modes + //---------------------------------------------------------------------- + typedef enum RunMode { + eOnlyThisThread, + eAllThreads, + eOnlyDuringStepping + } RunMode; + + //---------------------------------------------------------------------- + // Byte ordering definitions + //---------------------------------------------------------------------- + typedef enum ByteOrder + { + eByteOrderInvalid = 0, + eByteOrderBig = 1, + eByteOrderPDP = 2, + eByteOrderLittle = 4 + } ByteOrder; + + //---------------------------------------------------------------------- + // Register encoding definitions + //---------------------------------------------------------------------- + typedef enum Encoding + { + eEncodingInvalid = 0, + eEncodingUint, // unsigned integer + eEncodingSint, // signed integer + eEncodingIEEE754, // float + eEncodingVector // vector registers + } Encoding; + + //---------------------------------------------------------------------- + // Display format definitions + //---------------------------------------------------------------------- + typedef enum Format + { + eFormatDefault = 0, + eFormatInvalid = 0, + eFormatBoolean, + eFormatBinary, + eFormatBytes, + eFormatBytesWithASCII, + eFormatChar, + eFormatCharPrintable, // Only printable characters, space if not printable + eFormatComplex, // Floating point complex type + eFormatComplexFloat = eFormatComplex, + eFormatCString, // NULL terminated C strings + eFormatDecimal, + eFormatEnum, + eFormatHex, + eFormatHexUppercase, + eFormatFloat, + eFormatOctal, + eFormatOSType, // OS character codes encoded into an integer 'PICT' 'text' etc... + eFormatUnicode16, + eFormatUnicode32, + eFormatUnsigned, + eFormatPointer, + eFormatVectorOfChar, + eFormatVectorOfSInt8, + eFormatVectorOfUInt8, + eFormatVectorOfSInt16, + eFormatVectorOfUInt16, + eFormatVectorOfSInt32, + eFormatVectorOfUInt32, + eFormatVectorOfSInt64, + eFormatVectorOfUInt64, + eFormatVectorOfFloat32, + eFormatVectorOfFloat64, + eFormatVectorOfUInt128, + eFormatComplexInteger, // Integer complex type + eFormatCharArray, // Print characters with no single quotes, used for character arrays that can contain non printable characters + eFormatAddressInfo, // Describe what an address points to (func + offset with file/line, symbol + offset, data, etc) + eFormatHexFloat, // ISO C99 hex float string + eFormatInstruction, // Disassemble an opcode + eFormatVoid, // Do not print this + kNumFormats + } Format; + + //---------------------------------------------------------------------- + // Description levels for "void GetDescription(Stream *, DescriptionLevel)" calls + //---------------------------------------------------------------------- + typedef enum DescriptionLevel + { + eDescriptionLevelBrief = 0, + eDescriptionLevelFull, + eDescriptionLevelVerbose, + eDescriptionLevelInitial, + kNumDescriptionLevels + } DescriptionLevel; + + //---------------------------------------------------------------------- + // Script interpreter types + //---------------------------------------------------------------------- + typedef enum ScriptLanguage + { + eScriptLanguageNone, + eScriptLanguagePython, + eScriptLanguageDefault = eScriptLanguagePython + } ScriptLanguage; + + //---------------------------------------------------------------------- + // Register numbering types + //---------------------------------------------------------------------- + typedef enum RegisterKind + { + eRegisterKindGCC = 0, // the register numbers seen in eh_frame + eRegisterKindDWARF, // the register numbers seen DWARF + eRegisterKindGeneric, // insn ptr reg, stack ptr reg, etc not specific to any particular target + eRegisterKindGDB, // the register numbers gdb uses (matches stabs numbers?) + eRegisterKindLLDB, // lldb's internal register numbers + kNumRegisterKinds + } RegisterKind; + + //---------------------------------------------------------------------- + // Thread stop reasons + //---------------------------------------------------------------------- + typedef enum StopReason + { + eStopReasonInvalid = 0, + eStopReasonNone, + eStopReasonTrace, + eStopReasonBreakpoint, + eStopReasonWatchpoint, + eStopReasonSignal, + eStopReasonException, + eStopReasonExec, // Program was re-exec'ed + eStopReasonPlanComplete, + eStopReasonThreadExiting + } StopReason; + + //---------------------------------------------------------------------- + // Command Return Status Types + //---------------------------------------------------------------------- + typedef enum ReturnStatus + { + eReturnStatusInvalid, + eReturnStatusSuccessFinishNoResult, + eReturnStatusSuccessFinishResult, + eReturnStatusSuccessContinuingNoResult, + eReturnStatusSuccessContinuingResult, + eReturnStatusStarted, + eReturnStatusFailed, + eReturnStatusQuit + } ReturnStatus; + + + //---------------------------------------------------------------------- + // Connection Status Types + //---------------------------------------------------------------------- + typedef enum ConnectionStatus + { + eConnectionStatusSuccess, // Success + eConnectionStatusEndOfFile, // End-of-file encountered + eConnectionStatusError, // Check GetError() for details + eConnectionStatusTimedOut, // Request timed out + eConnectionStatusNoConnection, // No connection + eConnectionStatusLostConnection // Lost connection while connected to a valid connection + } ConnectionStatus; + + typedef enum ErrorType + { + eErrorTypeInvalid, + eErrorTypeGeneric, ///< Generic errors that can be any value. + eErrorTypeMachKernel, ///< Mach kernel error codes. + eErrorTypePOSIX ///< POSIX error codes. + } ErrorType; + + + typedef enum ValueType + { + eValueTypeInvalid = 0, + eValueTypeVariableGlobal = 1, // globals variable + eValueTypeVariableStatic = 2, // static variable + eValueTypeVariableArgument = 3, // function argument variables + eValueTypeVariableLocal = 4, // function local variables + eValueTypeRegister = 5, // stack frame register value + eValueTypeRegisterSet = 6, // A collection of stack frame register values + eValueTypeConstResult = 7 // constant result variables + } ValueType; + + //---------------------------------------------------------------------- + // Token size/granularities for Input Readers + //---------------------------------------------------------------------- + + typedef enum InputReaderGranularity + { + eInputReaderGranularityInvalid = 0, + eInputReaderGranularityByte, + eInputReaderGranularityWord, + eInputReaderGranularityLine, + eInputReaderGranularityAll + } InputReaderGranularity; + + //------------------------------------------------------------------ + /// These mask bits allow a common interface for queries that can + /// limit the amount of information that gets parsed to only the + /// information that is requested. These bits also can indicate what + /// actually did get resolved during query function calls. + /// + /// Each definition corresponds to a one of the member variables + /// in this class, and requests that that item be resolved, or + /// indicates that the member did get resolved. + //------------------------------------------------------------------ + typedef enum SymbolContextItem + { + eSymbolContextTarget = (1u << 0), ///< Set when \a target is requested from a query, or was located in query results + eSymbolContextModule = (1u << 1), ///< Set when \a module is requested from a query, or was located in query results + eSymbolContextCompUnit = (1u << 2), ///< Set when \a comp_unit is requested from a query, or was located in query results + eSymbolContextFunction = (1u << 3), ///< Set when \a function is requested from a query, or was located in query results + eSymbolContextBlock = (1u << 4), ///< Set when the deepest \a block is requested from a query, or was located in query results + eSymbolContextLineEntry = (1u << 5), ///< Set when \a line_entry is requested from a query, or was located in query results + eSymbolContextSymbol = (1u << 6), ///< Set when \a symbol is requested from a query, or was located in query results + eSymbolContextEverything = ((eSymbolContextSymbol << 1) - 1u) ///< Indicates to try and lookup everything up during a query. + } SymbolContextItem; + + typedef enum Permissions + { + ePermissionsWritable = (1u << 0), + ePermissionsReadable = (1u << 1), + ePermissionsExecutable = (1u << 2) + } Permissions; + + typedef enum InputReaderAction + { + eInputReaderActivate, // reader is newly pushed onto the reader stack + eInputReaderAsynchronousOutputWritten, // an async output event occurred; the reader may want to do something + eInputReaderReactivate, // reader is on top of the stack again after another reader was popped off + eInputReaderDeactivate, // another reader was pushed on the stack + eInputReaderGotToken, // reader got one of its tokens (granularity) + eInputReaderInterrupt, // reader received an interrupt signal (probably from a control-c) + eInputReaderEndOfFile, // reader received an EOF char (probably from a control-d) + eInputReaderDone // reader was just popped off the stack and is done + } InputReaderAction; + + typedef enum BreakpointEventType + { + eBreakpointEventTypeInvalidType = (1u << 0), + eBreakpointEventTypeAdded = (1u << 1), + eBreakpointEventTypeRemoved = (1u << 2), + eBreakpointEventTypeLocationsAdded = (1u << 3), // Locations added doesn't get sent when the breakpoint is created + eBreakpointEventTypeLocationsRemoved = (1u << 4), + eBreakpointEventTypeLocationsResolved = (1u << 5), + eBreakpointEventTypeEnabled = (1u << 6), + eBreakpointEventTypeDisabled = (1u << 7), + eBreakpointEventTypeCommandChanged = (1u << 8), + eBreakpointEventTypeConditionChanged = (1u << 9), + eBreakpointEventTypeIgnoreChanged = (1u << 10), + eBreakpointEventTypeThreadChanged = (1u << 11) + } BreakpointEventType; + + typedef enum WatchpointEventType + { + eWatchpointEventTypeInvalidType = (1u << 0), + eWatchpointEventTypeAdded = (1u << 1), + eWatchpointEventTypeRemoved = (1u << 2), + eWatchpointEventTypeEnabled = (1u << 6), + eWatchpointEventTypeDisabled = (1u << 7), + eWatchpointEventTypeCommandChanged = (1u << 8), + eWatchpointEventTypeConditionChanged = (1u << 9), + eWatchpointEventTypeIgnoreChanged = (1u << 10), + eWatchpointEventTypeThreadChanged = (1u << 11), + eWatchpointEventTypeTypeChanged = (1u << 12) + } WatchpointEventType; + + + //---------------------------------------------------------------------- + /// Programming language type. + /// + /// These enumerations use the same language enumerations as the DWARF + /// specification for ease of use and consistency. + /// The enum -> string code is in LanguageRuntime.cpp, don't change this + /// table without updating that code as well. + //---------------------------------------------------------------------- + typedef enum LanguageType + { + eLanguageTypeUnknown = 0x0000, ///< Unknown or invalid language value. + eLanguageTypeC89 = 0x0001, ///< ISO C:1989. + eLanguageTypeC = 0x0002, ///< Non-standardized C, such as K&R. + eLanguageTypeAda83 = 0x0003, ///< ISO Ada:1983. + eLanguageTypeC_plus_plus = 0x0004, ///< ISO C++:1998. + eLanguageTypeCobol74 = 0x0005, ///< ISO Cobol:1974. + eLanguageTypeCobol85 = 0x0006, ///< ISO Cobol:1985. + eLanguageTypeFortran77 = 0x0007, ///< ISO Fortran 77. + eLanguageTypeFortran90 = 0x0008, ///< ISO Fortran 90. + eLanguageTypePascal83 = 0x0009, ///< ISO Pascal:1983. + eLanguageTypeModula2 = 0x000a, ///< ISO Modula-2:1996. + eLanguageTypeJava = 0x000b, ///< Java. + eLanguageTypeC99 = 0x000c, ///< ISO C:1999. + eLanguageTypeAda95 = 0x000d, ///< ISO Ada:1995. + eLanguageTypeFortran95 = 0x000e, ///< ISO Fortran 95. + eLanguageTypePLI = 0x000f, ///< ANSI PL/I:1976. + eLanguageTypeObjC = 0x0010, ///< Objective-C. + eLanguageTypeObjC_plus_plus = 0x0011, ///< Objective-C++. + eLanguageTypeUPC = 0x0012, ///< Unified Parallel C. + eLanguageTypeD = 0x0013, ///< D. + eLanguageTypePython = 0x0014, ///< Python. + eNumLanguageTypes + } LanguageType; + + typedef enum DynamicValueType + { + eNoDynamicValues = 0, + eDynamicCanRunTarget = 1, + eDynamicDontRunTarget = 2 + } DynamicValueType; + + typedef enum AccessType + { + eAccessNone, + eAccessPublic, + eAccessPrivate, + eAccessProtected, + eAccessPackage + } AccessType; + + typedef enum CommandArgumentType + { + eArgTypeAddress = 0, + eArgTypeAddressOrExpression, + eArgTypeAliasName, + eArgTypeAliasOptions, + eArgTypeArchitecture, + eArgTypeBoolean, + eArgTypeBreakpointID, + eArgTypeBreakpointIDRange, + eArgTypeByteSize, + eArgTypeClassName, + eArgTypeCommandName, + eArgTypeCount, + eArgTypeDirectoryName, + eArgTypeDisassemblyFlavor, + eArgTypeEndAddress, + eArgTypeExpression, + eArgTypeExpressionPath, + eArgTypeExprFormat, + eArgTypeFilename, + eArgTypeFormat, + eArgTypeFrameIndex, + eArgTypeFullName, + eArgTypeFunctionName, + eArgTypeFunctionOrSymbol, + eArgTypeGDBFormat, + eArgTypeIndex, + eArgTypeLanguage, + eArgTypeLineNum, + eArgTypeLogCategory, + eArgTypeLogChannel, + eArgTypeMethod, + eArgTypeName, + eArgTypeNewPathPrefix, + eArgTypeNumLines, + eArgTypeNumberPerLine, + eArgTypeOffset, + eArgTypeOldPathPrefix, + eArgTypeOneLiner, + eArgTypePid, + eArgTypePlugin, + eArgTypeProcessName, + eArgTypePythonClass, + eArgTypePythonFunction, + eArgTypePythonScript, + eArgTypeQueueName, + eArgTypeRegisterName, + eArgTypeRegularExpression, + eArgTypeRunArgs, + eArgTypeRunMode, + eArgTypeScriptedCommandSynchronicity, + eArgTypeScriptLang, + eArgTypeSearchWord, + eArgTypeSelector, + eArgTypeSettingIndex, + eArgTypeSettingKey, + eArgTypeSettingPrefix, + eArgTypeSettingVariableName, + eArgTypeShlibName, + eArgTypeSourceFile, + eArgTypeSortOrder, + eArgTypeStartAddress, + eArgTypeSummaryString, + eArgTypeSymbol, + eArgTypeThreadID, + eArgTypeThreadIndex, + eArgTypeThreadName, + eArgTypeUnsignedInteger, + eArgTypeUnixSignal, + eArgTypeVarName, + eArgTypeValue, + eArgTypeWidth, + eArgTypeNone, + eArgTypePlatform, + eArgTypeWatchpointID, + eArgTypeWatchpointIDRange, + eArgTypeWatchType, + eArgTypeLastArg // Always keep this entry as the last entry in this enumeration!! + } CommandArgumentType; + + //---------------------------------------------------------------------- + // Symbol types + //---------------------------------------------------------------------- + typedef enum SymbolType + { + eSymbolTypeAny = 0, + eSymbolTypeInvalid = 0, + eSymbolTypeAbsolute, + eSymbolTypeCode, + eSymbolTypeResolver, + eSymbolTypeData, + eSymbolTypeTrampoline, + eSymbolTypeRuntime, + eSymbolTypeException, + eSymbolTypeSourceFile, + eSymbolTypeHeaderFile, + eSymbolTypeObjectFile, + eSymbolTypeCommonBlock, + eSymbolTypeBlock, + eSymbolTypeLocal, + eSymbolTypeParam, + eSymbolTypeVariable, + eSymbolTypeVariableType, + eSymbolTypeLineEntry, + eSymbolTypeLineHeader, + eSymbolTypeScopeBegin, + eSymbolTypeScopeEnd, + eSymbolTypeAdditional, // When symbols take more than one entry, the extra entries get this type + eSymbolTypeCompiler, + eSymbolTypeInstrumentation, + eSymbolTypeUndefined, + eSymbolTypeObjCClass, + eSymbolTypeObjCMetaClass, + eSymbolTypeObjCIVar + } SymbolType; + + typedef enum SectionType + { + eSectionTypeInvalid, + eSectionTypeCode, + eSectionTypeContainer, // The section contains child sections + eSectionTypeData, + eSectionTypeDataCString, // Inlined C string data + eSectionTypeDataCStringPointers, // Pointers to C string data + eSectionTypeDataSymbolAddress, // Address of a symbol in the symbol table + eSectionTypeData4, + eSectionTypeData8, + eSectionTypeData16, + eSectionTypeDataPointers, + eSectionTypeDebug, + eSectionTypeZeroFill, + eSectionTypeDataObjCMessageRefs, // Pointer to function pointer + selector + eSectionTypeDataObjCCFStrings, // Objective C const CFString/NSString objects + eSectionTypeDWARFDebugAbbrev, + eSectionTypeDWARFDebugAranges, + eSectionTypeDWARFDebugFrame, + eSectionTypeDWARFDebugInfo, + eSectionTypeDWARFDebugLine, + eSectionTypeDWARFDebugLoc, + eSectionTypeDWARFDebugMacInfo, + eSectionTypeDWARFDebugPubNames, + eSectionTypeDWARFDebugPubTypes, + eSectionTypeDWARFDebugRanges, + eSectionTypeDWARFDebugStr, + eSectionTypeDWARFAppleNames, + eSectionTypeDWARFAppleTypes, + eSectionTypeDWARFAppleNamespaces, + eSectionTypeDWARFAppleObjC, + eSectionTypeELFSymbolTable, // Elf SHT_SYMTAB section + eSectionTypeELFDynamicSymbols, // Elf SHT_DYNSYM section + eSectionTypeELFRelocationEntries, // Elf SHT_REL or SHT_REL section + eSectionTypeELFDynamicLinkInfo, // Elf SHT_DYNAMIC section + eSectionTypeEHFrame, + eSectionTypeOther + + } SectionType; + + typedef enum EmulateInstructionOptions + { + eEmulateInstructionOptionNone = (0u), + eEmulateInstructionOptionAutoAdvancePC = (1u << 0), + eEmulateInstructionOptionIgnoreConditions = (1u << 1) + } EmulateInstructionOptions; + + typedef enum FunctionNameType + { + eFunctionNameTypeNone = 0u, + eFunctionNameTypeAuto = (1u << 1), // Automatically figure out which FunctionNameType + // bits to set based on the function name. + eFunctionNameTypeFull = (1u << 2), // The function name. + // For C this is the same as just the name of the function + // For C++ this is the mangled or demangled version of the mangled name. + // For ObjC this is the full function signature with the + or + // - and the square brackets and the class and selector + eFunctionNameTypeBase = (1u << 3), // The function name only, no namespaces or arguments and no class + // methods or selectors will be searched. + eFunctionNameTypeMethod = (1u << 4), // Find function by method name (C++) with no namespace or arguments + eFunctionNameTypeSelector = (1u << 5), // Find function by selector name (ObjC) names + eFunctionNameTypeAny = eFunctionNameTypeAuto // DEPRECATED: use eFunctionNameTypeAuto + } FunctionNameType; + + + //---------------------------------------------------------------------- + // Basic types enumeration for the public API SBType::GetBasicType() + //---------------------------------------------------------------------- + typedef enum BasicType + { + eBasicTypeInvalid = 0, + eBasicTypeVoid = 1, + eBasicTypeChar, + eBasicTypeSignedChar, + eBasicTypeUnsignedChar, + eBasicTypeWChar, + eBasicTypeSignedWChar, + eBasicTypeUnsignedWChar, + eBasicTypeChar16, + eBasicTypeChar32, + eBasicTypeShort, + eBasicTypeUnsignedShort, + eBasicTypeInt, + eBasicTypeUnsignedInt, + eBasicTypeLong, + eBasicTypeUnsignedLong, + eBasicTypeLongLong, + eBasicTypeUnsignedLongLong, + eBasicTypeInt128, + eBasicTypeUnsignedInt128, + eBasicTypeBool, + eBasicTypeHalf, + eBasicTypeFloat, + eBasicTypeDouble, + eBasicTypeLongDouble, + eBasicTypeFloatComplex, + eBasicTypeDoubleComplex, + eBasicTypeLongDoubleComplex, + eBasicTypeObjCID, + eBasicTypeObjCClass, + eBasicTypeObjCSel, + eBasicTypeNullPtr, + eBasicTypeOther + } BasicType; + + typedef enum TypeClass + { + eTypeClassInvalid = (0u), + eTypeClassArray = (1u << 0), + eTypeClassBlockPointer = (1u << 1), + eTypeClassBuiltin = (1u << 2), + eTypeClassClass = (1u << 3), + eTypeClassComplexFloat = (1u << 4), + eTypeClassComplexInteger = (1u << 5), + eTypeClassEnumeration = (1u << 6), + eTypeClassFunction = (1u << 7), + eTypeClassMemberPointer = (1u << 8), + eTypeClassObjCObject = (1u << 9), + eTypeClassObjCInterface = (1u << 10), + eTypeClassObjCObjectPointer = (1u << 11), + eTypeClassPointer = (1u << 12), + eTypeClassReference = (1u << 13), + eTypeClassStruct = (1u << 14), + eTypeClassTypedef = (1u << 15), + eTypeClassUnion = (1u << 16), + eTypeClassVector = (1u << 17), + // Define the last type class as the MSBit of a 32 bit value + eTypeClassOther = (1u << 31), + // Define a mask that can be used for any type when finding types + eTypeClassAny = (0xffffffffu) + } TypeClass; + + typedef enum TemplateArgumentKind + { + eTemplateArgumentKindNull = 0, + eTemplateArgumentKindType, + eTemplateArgumentKindDeclaration, + eTemplateArgumentKindIntegral, + eTemplateArgumentKindTemplate, + eTemplateArgumentKindTemplateExpansion, + eTemplateArgumentKindExpression, + eTemplateArgumentKindPack + + } TemplateArgumentKind; + + //---------------------------------------------------------------------- + // Options that can be set for a formatter to alter its behavior + // Not all of these are applicable to all formatter types + //---------------------------------------------------------------------- + typedef enum TypeOptions + { + eTypeOptionNone = (0u), + eTypeOptionCascade = (1u << 0), + eTypeOptionSkipPointers = (1u << 1), + eTypeOptionSkipReferences = (1u << 2), + eTypeOptionHideChildren = (1u << 3), + eTypeOptionHideValue = (1u << 4), + eTypeOptionShowOneLiner = (1u << 5), + eTypeOptionHideNames = (1u << 6) + } TypeOptions; + + //---------------------------------------------------------------------- + // This is the return value for frame comparisons. When frame A pushes + // frame B onto the stack, frame A is OLDER than frame B. + //---------------------------------------------------------------------- + typedef enum FrameComparison + { + eFrameCompareInvalid, + eFrameCompareUnknown, + eFrameCompareEqual, + eFrameCompareYounger, + eFrameCompareOlder + } FrameComparison; + + //---------------------------------------------------------------------- + // Address Class + // + // A way of classifying an address used for disassembling and setting + // breakpoints. Many object files can track exactly what parts of their + // object files are code, data and other information. This is of course + // above and beyond just looking at the section types. For example, code + // might contain PC relative data and the object file might be able to + // tell us that an address in code is data. + //---------------------------------------------------------------------- + typedef enum AddressClass + { + eAddressClassInvalid, + eAddressClassUnknown, + eAddressClassCode, + eAddressClassCodeAlternateISA, + eAddressClassData, + eAddressClassDebug, + eAddressClassRuntime + } AddressClass; + +} // namespace lldb + + +#endif // LLDB_lldb_enumerations_h_ diff --git a/include/lldb/lldb-forward.h b/include/lldb/lldb-forward.h new file mode 100644 index 00000000000..84af8b64690 --- /dev/null +++ b/include/lldb/lldb-forward.h @@ -0,0 +1,378 @@ +//===-- lldb-forward.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_forward_h_ +#define LLDB_lldb_forward_h_ + +#if defined(__cplusplus) + +#include "lldb/Utility/SharingPtr.h" + +//---------------------------------------------------------------------- +// lldb forward declarations +//---------------------------------------------------------------------- +namespace lldb_private { + +class ABI; +class Address; +class AddressImpl; +class AddressRange; +class AddressResolver; +class ArchSpec; +class Args; +class ASTResultSynthesizer; +class Baton; +class Block; +class Breakpoint; +class BreakpointID; +class BreakpointIDList; +class BreakpointList; +class BreakpointLocation; +class BreakpointLocationCollection; +class BreakpointLocationList; +class BreakpointOptions; +class BreakpointResolver; +class BreakpointSite; +class BreakpointSiteList; +class BroadcastEventSpec; +class Broadcaster; +class BroadcasterManager; +class CPPLanguageRuntime; +class ClangASTContext; +class ClangASTImporter; +class ClangASTMetadata; +class ClangASTSource; +class ClangASTType; +class ClangNamespaceDecl; +class ClangExpression; +class ClangExpressionDeclMap; +class ClangExpressionParser; +class ClangExpressionVariable; +class ClangExpressionVariableList; +class ClangExpressionVariableList; +class ClangExpressionVariables; +class ClangFunction; +class ClangPersistentVariables; +class ClangUserExpression; +class ClangUtilityFunction; +class CommandInterpreter; +class CommandObject; +class CommandReturnObject; +class Communication; +class CompileUnit; +class Condition; +class Connection; +class ConnectionFileDescriptor; +class ConstString; +class CXXSyntheticChildren; +class DWARFCallFrameInfo; +class DWARFExpression; +class DataBuffer; +class DataEncoder; +class DataExtractor; +class Debugger; +class Declaration; +class Disassembler; +class DynamicLibrary; +class DynamicLoader; +class EmulateInstruction; +class Error; +class EvaluateExpressionOptions; +class Event; +class EventData; +class ExecutionContext; +class ExecutionContextRef; +class ExecutionContextRefLocker; +class ExecutionContextScope; +class FileSpec; +class FileSpecList; +class Flags; +class TypeCategoryImpl; +class FormatManager; +class FuncUnwinders; +class Function; +class FunctionInfo; +class InlineFunctionInfo; +class InputReader; +class Instruction; +class InstructionList; +class IRExecutionUnit; +class LanguageRuntime; +class LineTable; +class Listener; +class Log; +class LogChannel; +class Mangled; +class Materializer; +class Module; +class ModuleList; +class ModuleSpec; +class ModuleSpecList; +class Mutex; +struct NameSearchContext; +class ObjCLanguageRuntime; +class ObjectContainer; +class OptionGroup; +class OptionGroupPlatform; +class ObjectFile; +class OperatingSystem; +class Options; +class OptionValue; +class OptionValueArch; +class OptionValueArgs; +class OptionValueArray; +class OptionValueBoolean; +class OptionValueDictionary; +class OptionValueEnumeration; +class OptionValueFileSpec; +class OptionValueFileSpecList; +class OptionValueFormat; +class OptionValuePathMappings; +class OptionValueProperties; +class OptionValueRegex; +class OptionValueSInt64; +class OptionValueString; +class OptionValueUInt64; +class OptionValueUUID; +class NamedOption; +class PathMappingList; +class Platform; +class Process; +class ProcessAttachInfo; +class ProcessModID; +class ProcessInfo; +class ProcessInstanceInfo; +class ProcessInstanceInfoList; +class ProcessInstanceInfoMatch; +class ProcessLaunchInfo; +class Property; +struct PropertyDefinition; +class PythonArray; +class PythonDictionary; +class PythonInteger; +class PythonObject; +class PythonString; +class RegisterContext; +class RegisterLocation; +class RegisterLocationList; +class RegisterValue; +class RegularExpression; +class Scalar; +class ScriptInterpreter; +class ScriptInterpreterLocker; +class ScriptInterpreterObject; +#ifndef LLDB_DISABLE_PYTHON +class ScriptInterpreterPython; +struct ScriptSummaryFormat; +#endif +class SearchFilter; +class Section; +class SectionImpl; +class SectionList; +class Settings; +class SourceManager; +class SourceManagerImpl; +class StackFrame; +class StackFrameImpl; +class StackFrameList; +class StackID; +class StopInfo; +class Stoppoint; +class StoppointCallbackContext; +class StoppointLocation; +class Stream; +template class StreamBuffer; +class StreamFile; +class StreamString; +class StringList; +struct StringSummaryFormat; +class TypeSummaryImpl; +class Symbol; +class SymbolContext; +class SymbolContextList; +class SymbolContextScope; +class SymbolContextSpecifier; +class SymbolFile; +class SymbolFileType; +class SymbolVendor; +class Symtab; +class SyntheticChildren; +class SyntheticChildrenFrontEnd; +class TypeFilterImpl; +#ifndef LLDB_DISABLE_PYTHON +class ScriptedSyntheticChildren; +#endif +class Target; +class TargetList; +class Thread; +class ThreadList; +class ThreadPlan; +class ThreadPlanBase; +class ThreadPlanRunToAddress; +class ThreadPlanStepInstruction; +class ThreadPlanStepOut; +class ThreadPlanStepOverBreakpoint; +class ThreadPlanStepRange; +class ThreadPlanStepThrough; +class ThreadPlanTracer; +class ThreadSpec; +class TimeValue; +class Type; +class TypeCategoryMap; +class TypeImpl; +class TypeAndOrName; +class TypeList; +class TypeListImpl; +class TypeMemberImpl; +class TypeNameSpecifierImpl; +class UUID; +class Unwind; +class UnwindAssembly; +class UnwindPlan; +class UnwindTable; +class VMRange; +class Value; +class TypeFormatImpl; +class ValueList; +class ValueObject; +class ValueObjectChild; +class ValueObjectConstResult; +class ValueObjectConstResultChild; +class ValueObjectConstResultImpl; +class ValueObjectList; +class Variable; +class VariableList; +class Watchpoint; +class WatchpointList; +class WatchpointOptions; +struct LineEntry; + +} // namespace lldb_private + +//---------------------------------------------------------------------- +// lldb forward declarations +//---------------------------------------------------------------------- +namespace lldb { + + typedef std::shared_ptr ABISP; + typedef std::shared_ptr BatonSP; + typedef std::shared_ptr BlockSP; + typedef std::shared_ptr BreakpointSP; + typedef std::weak_ptr BreakpointWP; + typedef std::shared_ptr BreakpointSiteSP; + typedef std::weak_ptr BreakpointSiteWP; + typedef std::shared_ptr BreakpointLocationSP; + typedef std::weak_ptr BreakpointLocationWP; + typedef std::shared_ptr BreakpointResolverSP; + typedef std::shared_ptr BroadcasterSP; + typedef std::shared_ptr ClangExpressionVariableSP; + typedef std::shared_ptr CommandObjectSP; + typedef std::shared_ptr CommunicationSP; + typedef std::shared_ptr ConnectionSP; + typedef std::shared_ptr CompUnitSP; + typedef std::shared_ptr DataBufferSP; + typedef std::shared_ptr DataExtractorSP; + typedef std::shared_ptr DebuggerSP; + typedef std::weak_ptr DebuggerWP; + typedef std::shared_ptr DisassemblerSP; + typedef std::shared_ptr DynamicLibrarySP; + typedef std::shared_ptr DynamicLoaderSP; + typedef std::shared_ptr EventSP; + typedef std::shared_ptr ExecutionContextRefSP; + typedef std::shared_ptr FunctionSP; + typedef std::shared_ptr FuncUnwindersSP; + typedef std::shared_ptr InlineFunctionInfoSP; + typedef std::shared_ptr InputReaderSP; + typedef std::shared_ptr InstructionSP; + typedef std::shared_ptr LanguageRuntimeSP; + typedef std::shared_ptr LineTableSP; + typedef std::shared_ptr ListenerSP; + typedef std::shared_ptr LogChannelSP; + typedef std::shared_ptr ModuleSP; + typedef std::weak_ptr ModuleWP; + typedef std::shared_ptr ObjectFileSP; + typedef std::weak_ptr ObjectFileWP; + typedef std::shared_ptr OptionValueSP; + typedef std::weak_ptr OptionValueWP; + typedef std::shared_ptr OptionValueArchSP; + typedef std::shared_ptr OptionValueArgsSP; + typedef std::shared_ptr OptionValueArraySP; + typedef std::shared_ptr OptionValueBooleanSP; + typedef std::shared_ptr OptionValueDictionarySP; + typedef std::shared_ptr OptionValueFileSpecSP; + typedef std::shared_ptr OptionValueFileSpecListSP; + typedef std::shared_ptr OptionValueFormatSP; + typedef std::shared_ptr OptionValuePathMappingsSP; + typedef std::shared_ptr OptionValuePropertiesSP; + typedef std::shared_ptr OptionValueRegexSP; + typedef std::shared_ptr OptionValueSInt64SP; + typedef std::shared_ptr OptionValueStringSP; + typedef std::shared_ptr OptionValueUInt64SP; + typedef std::shared_ptr OptionValueUUIDSP; + typedef std::shared_ptr PlatformSP; + typedef std::shared_ptr ProcessSP; + typedef std::shared_ptr ProcessAttachInfoSP; + typedef std::shared_ptr ProcessLaunchInfoSP; + typedef std::weak_ptr ProcessWP; + typedef std::shared_ptr PropertySP; + typedef std::shared_ptr RegisterContextSP; + typedef std::shared_ptr RegularExpressionSP; + typedef std::shared_ptr ScriptInterpreterObjectSP; +#ifndef LLDB_DISABLE_PYTHON + typedef std::shared_ptr ScriptSummaryFormatSP; +#endif // #ifndef LLDB_DISABLE_PYTHON + typedef std::shared_ptr SectionSP; + typedef std::weak_ptr SectionWP; + typedef std::shared_ptr SearchFilterSP; + typedef std::shared_ptr SettingsSP; + typedef std::shared_ptr StackFrameSP; + typedef std::weak_ptr StackFrameWP; + typedef std::shared_ptr StackFrameListSP; + typedef std::shared_ptr StopInfoSP; + typedef std::shared_ptr StoppointLocationSP; + typedef std::shared_ptr StreamSP; + typedef std::weak_ptr StreamWP; + typedef std::shared_ptr StringTypeSummaryImplSP; + typedef std::shared_ptr SymbolFileSP; + typedef std::shared_ptr SymbolFileTypeSP; + typedef std::weak_ptr SymbolFileTypeWP; + typedef std::shared_ptr SymbolContextSpecifierSP; + typedef std::shared_ptr SyntheticChildrenSP; + typedef std::shared_ptr SyntheticChildrenFrontEndSP; + typedef std::shared_ptr TargetSP; + typedef std::weak_ptr TargetWP; + typedef std::shared_ptr ThreadSP; + typedef std::weak_ptr ThreadWP; + typedef std::shared_ptr ThreadPlanSP; + typedef std::shared_ptr ThreadPlanTracerSP; + typedef std::shared_ptr TypeSP; + typedef std::weak_ptr TypeWP; + typedef std::shared_ptr TypeCategoryImplSP; + typedef std::shared_ptr TypeImplSP; + typedef std::shared_ptr TypeFilterImplSP; + typedef std::shared_ptr TypeFormatImplSP; + typedef std::shared_ptr TypeNameSpecifierImplSP; + typedef std::shared_ptr TypeSummaryImplSP; +#ifndef LLDB_DISABLE_PYTHON + typedef std::shared_ptr ScriptedSyntheticChildrenSP; +#endif + typedef std::shared_ptr UnwindPlanSP; + typedef lldb_private::SharingPtr ValueObjectSP; + typedef std::shared_ptr ValueSP; + typedef std::shared_ptr ValueListSP; + typedef std::shared_ptr VariableSP; + typedef std::shared_ptr VariableListSP; + typedef std::shared_ptr ValueObjectListSP; + typedef std::shared_ptr WatchpointSP; + +} // namespace lldb + + +#endif // #if defined(__cplusplus) +#endif // LLDB_lldb_forward_h_ diff --git a/include/lldb/lldb-private-enumerations.h b/include/lldb/lldb-private-enumerations.h new file mode 100644 index 00000000000..60c90907145 --- /dev/null +++ b/include/lldb/lldb-private-enumerations.h @@ -0,0 +1,245 @@ +//===-- lldb-private-enumerations.h -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_private_enumerations_h_ +#define LLDB_lldb_private_enumerations_h_ + +namespace lldb_private { + +//---------------------------------------------------------------------- +// Thread Step Types +//---------------------------------------------------------------------- +typedef enum StepType +{ + eStepTypeNone, + eStepTypeTrace, ///< Single step one instruction. + eStepTypeTraceOver, ///< Single step one instruction, stepping over. + eStepTypeInto, ///< Single step into a specified context. + eStepTypeOver, ///< Single step over a specified context. + eStepTypeOut ///< Single step out a specified context. +} StepType; + +//---------------------------------------------------------------------- +// Address Types +//---------------------------------------------------------------------- +typedef enum AddressType +{ + eAddressTypeInvalid = 0, + eAddressTypeFile, ///< Address is an address as found in an object or symbol file + eAddressTypeLoad, ///< Address is an address as in the current target inferior process + eAddressTypeHost ///< Address is an address in the process that is running this code +} AddressType; + +//---------------------------------------------------------------------- +// Votes - Need a tri-state, yes, no, no opinion... +//---------------------------------------------------------------------- +typedef enum Vote +{ + eVoteNo = -1, + eVoteNoOpinion = 0, + eVoteYes = 1 +} Vote; + +typedef enum ArchitectureType +{ + eArchTypeInvalid, + eArchTypeMachO, + eArchTypeELF, + kNumArchTypes +} ArchitectureType; + +//---------------------------------------------------------------------- +/// Settable state variable types. +/// +//---------------------------------------------------------------------- + +//typedef enum SettableVariableType +//{ +// eSetVarTypeInt, +// eSetVarTypeBoolean, +// eSetVarTypeString, +// eSetVarTypeArray, +// eSetVarTypeDictionary, +// eSetVarTypeEnum, +// eSetVarTypeNone +//} SettableVariableType; + +typedef enum VarSetOperationType +{ + eVarSetOperationReplace, + eVarSetOperationInsertBefore, + eVarSetOperationInsertAfter, + eVarSetOperationRemove, + eVarSetOperationAppend, + eVarSetOperationClear, + eVarSetOperationAssign, + eVarSetOperationInvalid +} VarSetOperationType; + +typedef enum ArgumentRepetitionType +{ + eArgRepeatPlain, // Exactly one occurrence + eArgRepeatOptional, // At most one occurrence, but it's optional + eArgRepeatPlus, // One or more occurrences + eArgRepeatStar, // Zero or more occurrences + eArgRepeatRange, // Repetition of same argument, from 1 to n + eArgRepeatPairPlain, // A pair of arguments that must always go together ([arg-type arg-value]), occurs exactly once + eArgRepeatPairOptional, // A pair that occurs at most once (optional) + eArgRepeatPairPlus, // One or more occurrences of a pair + eArgRepeatPairStar, // Zero or more occurrences of a pair + eArgRepeatPairRange, // A pair that repeats from 1 to n + eArgRepeatPairRangeOptional // A pair that repeats from 1 to n, but is optional +} ArgumentRepetitionType; + +typedef enum SortOrder +{ + eSortOrderNone, + eSortOrderByAddress, + eSortOrderByName +} SortOrder; + + +//---------------------------------------------------------------------- +// Used in conjunction with Host::GetLLDBPath () to find files that +// are related to +//---------------------------------------------------------------------- +typedef enum PathType +{ + ePathTypeLLDBShlibDir, // The directory where the lldb.so (unix) or LLDB mach-o file in LLDB.framework (MacOSX) exists + ePathTypeSupportExecutableDir, // Find LLDB support executable directory (debugserver, etc) + ePathTypeHeaderDir, // Find LLDB header file directory + ePathTypePythonDir, // Find Python modules (PYTHONPATH) directory + ePathTypeLLDBSystemPlugins, // System plug-ins directory + ePathTypeLLDBUserPlugins // User plug-ins directory +} PathType; + + +//---------------------------------------------------------------------- +// We can execute ThreadPlans on one thread with various fall-back modes +// (try other threads after timeout, etc.) This enum gives the result of +// thread plan executions. +//---------------------------------------------------------------------- +typedef enum ExecutionResults +{ + eExecutionSetupError, + eExecutionCompleted, + eExecutionDiscarded, + eExecutionInterrupted, + eExecutionHitBreakpoint, + eExecutionTimedOut +} ExecutionResults; + +typedef enum ObjCRuntimeVersions { + eObjC_VersionUnknown = 0, + eAppleObjC_V1 = 1, + eAppleObjC_V2 = 2 +} ObjCRuntimeVersions; + + +//---------------------------------------------------------------------- +// LazyBool is for boolean values that need to be calculated lazily. +// Values start off set to eLazyBoolCalculate, and then they can be +// calculated once and set to eLazyBoolNo or eLazyBoolYes. +//---------------------------------------------------------------------- +typedef enum LazyBool { + eLazyBoolCalculate = -1, + eLazyBoolNo = 0, + eLazyBoolYes = 1 +} LazyBool; + +//------------------------------------------------------------------ +/// Name matching +//------------------------------------------------------------------ +typedef enum NameMatchType +{ + eNameMatchIgnore, + eNameMatchEquals, + eNameMatchContains, + eNameMatchStartsWith, + eNameMatchEndsWith, + eNameMatchRegularExpression + +} NameMatchType; + + +//------------------------------------------------------------------ +/// Instruction types +//------------------------------------------------------------------ +typedef enum InstructionType +{ + eInstructionTypeAny, // Support for any instructions at all (at least one) + eInstructionTypePrologueEpilogue, // All prologue and epilogue instructons that push and pop register values and modify sp/fp + eInstructionTypePCModifying, // Any instruction that modifies the program counter/instruction pointer + eInstructionTypeAll // All instructions of any kind + +} InstructionType; + + +//------------------------------------------------------------------ +/// Format category entry types +//------------------------------------------------------------------ +typedef enum FormatCategoryItem +{ + eFormatCategoryItemSummary = 0x0001, + eFormatCategoryItemRegexSummary = 0x0002, + eFormatCategoryItemFilter = 0x0004, + eFormatCategoryItemRegexFilter = 0x0008, + eFormatCategoryItemSynth = 0x0010, + eFormatCategoryItemRegexSynth = 0x0020 +} FormatCategoryItem; + +//------------------------------------------------------------------ +/// Expression execution policies +//------------------------------------------------------------------ +typedef enum { + eExecutionPolicyOnlyWhenNeeded, + eExecutionPolicyNever, + eExecutionPolicyAlways +} ExecutionPolicy; + +//---------------------------------------------------------------------- +// Ways that the FormatManager picks a particular format for a type +//---------------------------------------------------------------------- +typedef enum FormatterChoiceCriterion +{ + eFormatterChoiceCriterionDirectChoice = 0x00000000, + eFormatterChoiceCriterionStrippedPointerReference = 0x00000001, + eFormatterChoiceCriterionNavigatedTypedefs = 0x00000002, + eFormatterChoiceCriterionRegularExpressionSummary = 0x00000004, + eFormatterChoiceCriterionRegularExpressionFilter = 0x00000004, + eFormatterChoiceCriterionDynamicObjCDiscovery = 0x00000008, + eFormatterChoiceCriterionStrippedBitField = 0x00000010, + eFormatterChoiceCriterionWentToStaticValue = 0x00000020 +} FormatterChoiceCriterion; + +//---------------------------------------------------------------------- +// Synchronicity behavior of scripted commands +//---------------------------------------------------------------------- +typedef enum ScriptedCommandSynchronicity +{ + eScriptedCommandSynchronicitySynchronous, + eScriptedCommandSynchronicityAsynchronous, + eScriptedCommandSynchronicityCurrentValue // use whatever the current synchronicity is +} ScriptedCommandSynchronicity; + + +//---------------------------------------------------------------------- +// Loading modules from memory +//---------------------------------------------------------------------- +typedef enum MemoryModuleLoadLevel { + eMemoryModuleLoadLevelMinimal, // Load sections only + eMemoryModuleLoadLevelPartial, // Load function bounds but no symbols + eMemoryModuleLoadLevelComplete, // Load sections and all symbols +} MemoryModuleLoadLevel; + + +} // namespace lldb_private + + +#endif // LLDB_lldb_private_enumerations_h_ diff --git a/include/lldb/lldb-private-interfaces.h b/include/lldb/lldb-private-interfaces.h new file mode 100644 index 00000000000..949cafed98b --- /dev/null +++ b/include/lldb/lldb-private-interfaces.h @@ -0,0 +1,46 @@ +//===-- lldb-private-interfaces.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_lldb_private_interfaces_h_ +#define liblldb_lldb_private_interfaces_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +namespace lldb_private +{ + typedef lldb::ABISP (*ABICreateInstance) (const ArchSpec &arch); + typedef Disassembler* (*DisassemblerCreateInstance) (const ArchSpec &arch, const char *flavor); + typedef DynamicLoader* (*DynamicLoaderCreateInstance) (Process* process, bool force); + typedef ObjectContainer* (*ObjectContainerCreateInstance) (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); + typedef size_t (*ObjectFileGetModuleSpecifications) (const FileSpec &file, lldb::DataBufferSP& data_sp, lldb::offset_t data_offset, lldb::offset_t file_offset, lldb::offset_t length, ModuleSpecList &module_specs); + typedef ObjectFile* (*ObjectFileCreateInstance) (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); + typedef ObjectFile* (*ObjectFileCreateMemoryInstance) (const lldb::ModuleSP &module_sp, lldb::DataBufferSP& data_sp, const lldb::ProcessSP &process_sp, lldb::addr_t offset); + typedef LogChannel* (*LogChannelCreateInstance) (); + typedef EmulateInstruction * (*EmulateInstructionCreateInstance) (const ArchSpec &arch, InstructionType inst_type); + typedef OperatingSystem* (*OperatingSystemCreateInstance) (Process *process, bool force); + typedef LanguageRuntime *(*LanguageRuntimeCreateInstance) (Process *process, lldb::LanguageType language); + typedef Platform* (*PlatformCreateInstance) (bool force, const ArchSpec *arch); + typedef lldb::ProcessSP (*ProcessCreateInstance) (Target &target, Listener &listener, const FileSpec *crash_file_path); + typedef SymbolFile* (*SymbolFileCreateInstance) (ObjectFile* obj_file); + typedef SymbolVendor* (*SymbolVendorCreateInstance) (const lldb::ModuleSP &module_sp, lldb_private::Stream *feedback_strm); // Module can be NULL for default system symbol vendor + typedef bool (*BreakpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id); + typedef bool (*WatchpointHitCallback) (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id); + typedef lldb::ThreadPlanSP (*ThreadPlanShouldStopHereCallback) (ThreadPlan *current_plan, Flags &flags, void *baton); + typedef UnwindAssembly* (*UnwindAssemblyCreateInstance) (const ArchSpec &arch); + typedef int (*ComparisonFunction)(const void *, const void *); + typedef bool (*CommandOverrideCallback)(void *baton, const char **argv); + typedef void (*DebuggerInitializeCallback)(Debugger &debugger); + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) + +#endif // liblldb_lldb_private_interfaces_h_ diff --git a/include/lldb/lldb-private-log.h b/include/lldb/lldb-private-log.h new file mode 100644 index 00000000000..31a1c23c5e6 --- /dev/null +++ b/include/lldb/lldb-private-log.h @@ -0,0 +1,90 @@ +//===-- lldb-private-log.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_lldb_private_log_h_ +#define liblldb_lldb_private_log_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" + +//---------------------------------------------------------------------- +// Log Bits specific to logging in lldb +//---------------------------------------------------------------------- +#define LIBLLDB_LOG_VERBOSE (1u << 0) +#define LIBLLDB_LOG_PROCESS (1u << 1) +#define LIBLLDB_LOG_THREAD (1u << 2) +#define LIBLLDB_LOG_DYNAMIC_LOADER (1u << 3) +#define LIBLLDB_LOG_EVENTS (1u << 4) +#define LIBLLDB_LOG_BREAKPOINTS (1u << 5) +#define LIBLLDB_LOG_WATCHPOINTS (1u << 6) +#define LIBLLDB_LOG_STEP (1u << 7) +#define LIBLLDB_LOG_EXPRESSIONS (1u << 8) +#define LIBLLDB_LOG_TEMPORARY (1u << 9) +#define LIBLLDB_LOG_STATE (1u << 10) +#define LIBLLDB_LOG_OBJECT (1u << 11) +#define LIBLLDB_LOG_COMMUNICATION (1u << 12) +#define LIBLLDB_LOG_CONNECTION (1u << 13) +#define LIBLLDB_LOG_HOST (1u << 14) +#define LIBLLDB_LOG_UNWIND (1u << 15) +#define LIBLLDB_LOG_API (1u << 16) +#define LIBLLDB_LOG_SCRIPT (1u << 17) +#define LIBLLDB_LOG_COMMANDS (1U << 18) +#define LIBLLDB_LOG_TYPES (1u << 19) +#define LIBLLDB_LOG_SYMBOLS (1u << 20) +#define LIBLLDB_LOG_MODULES (1u << 21) +#define LIBLLDB_LOG_TARGET (1u << 22) +#define LIBLLDB_LOG_MMAP (1u << 23) +#define LIBLLDB_LOG_OS (1u << 24) +#define LIBLLDB_LOG_ALL (UINT32_MAX) +#define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\ + LIBLLDB_LOG_THREAD |\ + LIBLLDB_LOG_DYNAMIC_LOADER |\ + LIBLLDB_LOG_BREAKPOINTS |\ + LIBLLDB_LOG_WATCHPOINTS |\ + LIBLLDB_LOG_STEP |\ + LIBLLDB_LOG_STATE |\ + LIBLLDB_LOG_SYMBOLS |\ + LIBLLDB_LOG_TARGET |\ + LIBLLDB_LOG_COMMANDS) + +namespace lldb_private { + +void +LogIfAllCategoriesSet (uint32_t mask, const char *format, ...); + +void +LogIfAnyCategoriesSet (uint32_t mask, const char *format, ...); + +Log * +GetLogIfAllCategoriesSet (uint32_t mask); + +Log * +GetLogIfAnyCategoriesSet (uint32_t mask); + +uint32_t +GetLogMask (); + +bool +IsLogVerbose (); + +void +DisableLog (const char **categories, Stream *feedback_strm); + +Log * +EnableLog (lldb::StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm); + +void +ListLogCategories (Stream *strm); + +} // namespace lldb_private + +#endif // liblldb_lldb_private_log_h_ diff --git a/include/lldb/lldb-private-types.h b/include/lldb/lldb-private-types.h new file mode 100644 index 00000000000..4340af114be --- /dev/null +++ b/include/lldb/lldb-private-types.h @@ -0,0 +1,74 @@ +//===-- lldb-private-types.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_lldb_private_types_h_ +#define liblldb_lldb_private_types_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-private.h" + +namespace lldb_private +{ + //---------------------------------------------------------------------- + // Every register is described in detail including its name, alternate + // name (optional), encoding, size in bytes and the default display + // format. + //---------------------------------------------------------------------- + typedef struct + { + const char *name; // Name of this register, can't be NULL + const char *alt_name; // Alternate name of this register, can be NULL + uint32_t byte_size; // Size in bytes of the register + uint32_t byte_offset; // The byte offset in the register context data where this register's value is found + lldb::Encoding encoding; // Encoding of the register bits + lldb::Format format; // Default display format + uint32_t kinds[lldb::kNumRegisterKinds]; // Holds all of the various register numbers for all register kinds + uint32_t *value_regs; // List of registers that must be terminated with LLDB_INVALID_REGNUM + uint32_t *invalidate_regs; // List of registers that must be invalidated when this register is modified, list must be terminated with LLDB_INVALID_REGNUM + } RegisterInfo; + + //---------------------------------------------------------------------- + // Registers are grouped into register sets + //---------------------------------------------------------------------- + typedef struct + { + const char *name; // Name of this register set + const char *short_name; // A short name for this register set + size_t num_registers; // The number of registers in REGISTERS array below + const uint32_t *registers; // An array of register numbers in this set + } RegisterSet; + + typedef struct + { + int64_t value; + const char *string_value; + const char *usage; + } OptionEnumValueElement; + + typedef struct + { + uint32_t usage_mask; // Used to mark options that can be used together. If (1 << n & usage_mask) != 0 + // then this option belongs to option set n. + bool required; // This option is required (in the current usage level) + const char *long_option; // Full name for this option. + int short_option; // Single character for this option. + int option_has_arg; // no_argument, required_argument or optional_argument + OptionEnumValueElement *enum_values; // If non-NULL an array of enum values. + uint32_t completion_type; // Cookie the option class can use to do define the argument completion. + lldb::CommandArgumentType argument_type; // Type of argument this option takes + const char *usage_text; // Full text explaining what this options does and what (if any) argument to + // pass it. + } OptionDefinition; + +} // namespace lldb_private + +#endif // #if defined(__cplusplus) + +#endif // liblldb_lldb_private_types_h_ diff --git a/include/lldb/lldb-private.h b/include/lldb/lldb-private.h new file mode 100644 index 00000000000..90590601d94 --- /dev/null +++ b/include/lldb/lldb-private.h @@ -0,0 +1,84 @@ +//===-- lldb-private.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef lldb_lldb_private_h_ +#define lldb_lldb_private_h_ + +#if defined(__cplusplus) + +#include "lldb/lldb-public.h" +#include "lldb/lldb-private-enumerations.h" +#include "lldb/lldb-private-interfaces.h" +#include "lldb/lldb-private-log.h" +#include "lldb/lldb-private-types.h" + +namespace lldb_private { + +//------------------------------------------------------------------ +/// Initializes lldb. +/// +/// This function should be called prior to using any lldb +/// classes to ensure they have a chance to do any static +/// initialization that they need to do. +//------------------------------------------------------------------ +void +Initialize(); + + +//------------------------------------------------------------------ +/// Notifies any classes that lldb will be terminating soon. +/// +/// This function will be called when the Debugger shared instance +/// is being destructed and will give classes the ability to clean +/// up any threads or other resources they have that they might not +/// be able to clean up in their own destructors. +/// +/// Internal classes that need this ability will need to add their +/// void T::WillTerminate() method in the body of this function in +/// lldb.cpp to ensure it will get called. +/// +/// TODO: when we start having external plug-ins, we will need a way +/// for plug-ins to register a WillTerminate callback. +//------------------------------------------------------------------ +void +WillTerminate(); + +//------------------------------------------------------------------ +/// Terminates lldb +/// +/// This function optionally can be called when clients are done +/// using lldb functionality to free up any static resources +/// that have been allocated during initialization or during +/// function calls. No lldb functions should be called after +/// calling this function without again calling DCInitialize() +/// again. +//------------------------------------------------------------------ +void +Terminate(); + + +const char * +GetVersion (); + +const char * +GetVoteAsCString (Vote vote); + +const char * +GetSectionTypeAsCString (lldb::SectionType sect_type); + +bool +NameMatches (const char *name, NameMatchType match_type, const char *match); + +} // namespace lldb_private + + +#endif // defined(__cplusplus) + + +#endif // lldb_lldb_private_h_ diff --git a/include/lldb/lldb-public.h b/include/lldb/lldb-public.h new file mode 100644 index 00000000000..b010edf5859 --- /dev/null +++ b/include/lldb/lldb-public.h @@ -0,0 +1,18 @@ +//===-- lldb-include.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_h_ +#define LLDB_lldb_h_ + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" +#include "lldb/lldb-types.h" + +#endif // LLDB_lldb_h_ diff --git a/include/lldb/lldb-python.h b/include/lldb/lldb-python.h new file mode 100644 index 00000000000..229e3967e4a --- /dev/null +++ b/include/lldb/lldb-python.h @@ -0,0 +1,29 @@ +//===-- lldb-python.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_python_h_ +#define LLDB_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 + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#endif // LLDB_DISABLE_PYTHON + +#endif // LLDB_lldb_python_h_ diff --git a/include/lldb/lldb-types.h b/include/lldb/lldb-types.h new file mode 100644 index 00000000000..2693c0c822b --- /dev/null +++ b/include/lldb/lldb-types.h @@ -0,0 +1,83 @@ +//===-- lldb-types.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_types_h_ +#define LLDB_lldb_types_h_ + +#include "lldb/lldb-enumerations.h" +#include "lldb/lldb-forward.h" + +#include +#include +#include +#include +#include +#include + +//---------------------------------------------------------------------- +// All host systems must define: +// lldb::condition_t The native condition type (or a substitute class) for conditions on the host system. +// lldb::mutex_t The native mutex type for mutex objects on the host system. +// lldb::thread_t The native thread type for spawned threads on the system +// lldb::thread_arg_t The type of the one any only thread creation argument for the host system +// lldb::thread_result_t The return type that gets returned when a thread finishes. +// lldb::thread_func_t The function prototype used to spawn a thread on the host system. +// #define LLDB_INVALID_PROCESS_ID ... +// #define LLDB_INVALID_THREAD_ID ... +// #define LLDB_INVALID_HOST_THREAD ... +// #define IS_VALID_LLDB_HOST_THREAD ... +//---------------------------------------------------------------------- + +// TODO: Add a bunch of ifdefs to determine the host system and what +// things should be defined. Currently MacOSX is being assumed by default +// since that is what lldb was first developed for. + +namespace lldb { + //---------------------------------------------------------------------- + // MacOSX Types + //---------------------------------------------------------------------- + typedef ::pthread_mutex_t mutex_t; + typedef pthread_cond_t condition_t; + typedef pthread_t thread_t; // Host thread type + typedef void * thread_arg_t; // Host thread argument type + typedef void * thread_result_t; // Host thread result type + typedef void * (*thread_func_t)(void *); // Host thread function type + typedef void (*LogOutputCallback) (const char *, void *baton); + typedef bool (*CommandOverrideCallback)(void *baton, const char **argv); +} // namespace lldb + +#if defined(__MINGW32__) + +const lldb::thread_t lldb_invalid_host_thread_const = { NULL, 0 } ; +#define LLDB_INVALID_HOST_THREAD (lldb_invalid_host_thread_const) +#define IS_VALID_LLDB_HOST_THREAD(t) (!(NULL == (t).p && 0 == (t).x)) + +#else + +#define LLDB_INVALID_HOST_THREAD ((lldb::thread_t)NULL) +#define IS_VALID_LLDB_HOST_THREAD(t) ((t) != LLDB_INVALID_HOST_THREAD) + +#endif + +#define LLDB_INVALID_HOST_TIME { 0, 0 } + +namespace lldb +{ + typedef uint64_t addr_t; + typedef uint64_t user_id_t; + typedef uint64_t pid_t; + typedef uint64_t tid_t; + typedef uint64_t offset_t; + typedef int32_t break_id_t; + typedef int32_t watch_id_t; + typedef void * clang_type_t; +} + + +#endif // LLDB_lldb_types_h_ diff --git a/include/lldb/lldb-versioning.h b/include/lldb/lldb-versioning.h new file mode 100644 index 00000000000..8ccc67d8e9c --- /dev/null +++ b/include/lldb/lldb-versioning.h @@ -0,0 +1,1607 @@ +//===-- lldb-versioning.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_versioning_h_ +#define LLDB_lldb_versioning_h_ + +//---------------------------------------------------------------------- +// LLDB API version +//---------------------------------------------------------------------- +#define LLDB_API_MAJOR_VERSION 1 +#define LLDB_API_MINOR_VERSION 0 + +/* + API versioning + --------------------------------- + + The LLDB API is versioned independently of the LLDB source base + Our API version numbers are composed of a major and a minor number + + The major number means a complete and stable revision of the API. Major numbers are compatibility breakers + (i.e. when we change the API major number, there is no promise of compatibility with the previous major version + and we are free to remove and/or change any APIs) + Minor numbers are a work-in-progress evolution of the API. APIs will not be removed or changed across minor versions + (minors do not break compatibility). However, we can deprecate APIs in minor versions or add new APIs in minor versions + A deprecated API is supposedly going to be removed in the next major version and will generate a warning if used + APIs we add in minor versions will not be removed (at least until the following major) but they might theoretically be deprecated + in a following minor version + Users are discouraged from using the LLDB version number to test for API features and should instead use the API version checking + as discussed below + + API version checking + --------------------------------- + + You can (optionally) sign into an API version checking feature + To do so you need to define three macros: + LLDB_API_CHECK_VERSIONING - define to any value (or no value) + LLDB_API_MAJOR_VERSION_WANTED - which major version of the LLDB API you are targeting + LLDB_API_MINOR_VERSION_WANTED - which minor version of the LLDB API you are targeting + + If these macros exist - LLDB will enable version checking of the public API + + If LLDB_API_MAJOR_VERSION is not equal to LLDB_API_MAJOR_VERSION_WANTED we will immediately halt your compilation with an error + This is by design, since we do not make any promise of compatibility across major versions - if you really want to test your luck, disable the versioning altogether + + If the major version test passes, you have signed up for a specific minor version of the API + Whenever we add or deprecate an API in a minor version, we will mark it with either + LLDB_API_NEW_IN_DOT_x - this API is new in LLDB .x + LLDB_API_DEPRECATED_IN_DOT_x - this API is deprecated as of .x + + If you are using an API new in DOT_x + if LLDB_API_MINOR_VERSION_WANTED >= x then all is well, else you will get a compilation error + This is meant to prevent you from using APIs that are newer than whatever LLDB you want to target + + If you are using an API deprecated in DOT_x + if LLDB_API_MINOR_VERSION_WANTED >= x then you will get a compilation warning, else all is well + This is meant to let you know that you are using an API that is deprecated and might go away + + Caveats + --------------------------------- + + Version checking only works on clang on OSX - you will get an error if you try to enable it on any other OS/compiler + If you want to enable version checking on other platforms, you will need to define appropriate implementations for + LLDB_API_IMPL_DEPRECATED and LLDB_API_IMPL_TOONEW and any other infrastructure your compiler needs for this purpose + + We have no deprecation-as-error mode + + There is no support for API versioning in Python + + We reserve to use macros whose names begin with LLDB_API_ and you should not use them in your source code as they might conflict + with present or future macro names we are using to implement versioning +*/ + +// if you want the version checking to work on other OS/compiler, define appropriate IMPL_DEPRECATED/IMPL_TOONEW +// and define LLDB_API_CHECK_VERSIONING_WORKS when you are ready to go live +#if defined(__APPLE__) && defined(__clang__) +#define LLDB_API_IMPL_DEPRECATED __attribute__((deprecated)) +#define LLDB_API_IMPL_TOONEW __attribute__((unavailable)) +#define LLDB_API_CHECK_VERSIONING_WORKS +#endif + +#if defined(LLDB_API_CHECK_VERSIONING) && !defined(LLDB_API_CHECK_VERSIONING_WORKS) +#error "API version checking will not work here - please disable or create and submit patches to lldb-versioning.h" +#endif + +#if defined(LLDB_API_CHECK_VERSIONING_WORKS) && (!defined(LLDB_API_IMPL_DEPRECATED) || !defined(LLDB_API_IMPL_TOONEW)) +#error "LLDB_API_CHECK_VERSIONING_WORKS needs LLDB_API_IMPL_DEPRECATED and LLDB_API_IMPL_TOONEW to be defined" +#endif + +#if defined(LLDB_API_CHECK_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) + +#if defined (LLDB_API_MAJOR_VERSION) && (LLDB_API_MAJOR_VERSION != LLDB_API_MAJOR_VERSION_WANTED) +#error "Cannot link using this LLDB version - public API versions are incompatible" +#endif + +#define LLDB_API_MINOR_VERSION_DOT_0 0 +#define LLDB_API_MINOR_VERSION_DOT_1 1 +#define LLDB_API_MINOR_VERSION_DOT_2 2 +#define LLDB_API_MINOR_VERSION_DOT_3 3 +#define LLDB_API_MINOR_VERSION_DOT_4 4 +#define LLDB_API_MINOR_VERSION_DOT_5 5 +#define LLDB_API_MINOR_VERSION_DOT_6 6 +#define LLDB_API_MINOR_VERSION_DOT_7 7 +#define LLDB_API_MINOR_VERSION_DOT_8 8 +#define LLDB_API_MINOR_VERSION_DOT_9 9 +#define LLDB_API_MINOR_VERSION_DOT_10 10 +#define LLDB_API_MINOR_VERSION_DOT_11 11 +#define LLDB_API_MINOR_VERSION_DOT_12 12 +#define LLDB_API_MINOR_VERSION_DOT_13 13 +#define LLDB_API_MINOR_VERSION_DOT_14 14 +#define LLDB_API_MINOR_VERSION_DOT_15 15 +#define LLDB_API_MINOR_VERSION_DOT_16 16 +#define LLDB_API_MINOR_VERSION_DOT_17 17 +#define LLDB_API_MINOR_VERSION_DOT_18 18 +#define LLDB_API_MINOR_VERSION_DOT_19 19 +#define LLDB_API_MINOR_VERSION_DOT_20 20 +#define LLDB_API_MINOR_VERSION_DOT_21 21 +#define LLDB_API_MINOR_VERSION_DOT_22 22 +#define LLDB_API_MINOR_VERSION_DOT_23 23 +#define LLDB_API_MINOR_VERSION_DOT_24 24 +#define LLDB_API_MINOR_VERSION_DOT_25 25 +#define LLDB_API_MINOR_VERSION_DOT_26 26 +#define LLDB_API_MINOR_VERSION_DOT_27 27 +#define LLDB_API_MINOR_VERSION_DOT_28 28 +#define LLDB_API_MINOR_VERSION_DOT_29 29 +#define LLDB_API_MINOR_VERSION_DOT_30 30 +#define LLDB_API_MINOR_VERSION_DOT_31 31 +#define LLDB_API_MINOR_VERSION_DOT_32 32 +#define LLDB_API_MINOR_VERSION_DOT_33 33 +#define LLDB_API_MINOR_VERSION_DOT_34 34 +#define LLDB_API_MINOR_VERSION_DOT_35 35 +#define LLDB_API_MINOR_VERSION_DOT_36 36 +#define LLDB_API_MINOR_VERSION_DOT_37 37 +#define LLDB_API_MINOR_VERSION_DOT_38 38 +#define LLDB_API_MINOR_VERSION_DOT_39 39 +#define LLDB_API_MINOR_VERSION_DOT_40 40 +#define LLDB_API_MINOR_VERSION_DOT_41 41 +#define LLDB_API_MINOR_VERSION_DOT_42 42 +#define LLDB_API_MINOR_VERSION_DOT_43 43 +#define LLDB_API_MINOR_VERSION_DOT_44 44 +#define LLDB_API_MINOR_VERSION_DOT_45 45 +#define LLDB_API_MINOR_VERSION_DOT_46 46 +#define LLDB_API_MINOR_VERSION_DOT_47 47 +#define LLDB_API_MINOR_VERSION_DOT_48 48 +#define LLDB_API_MINOR_VERSION_DOT_49 49 +#define LLDB_API_MINOR_VERSION_DOT_50 50 +#define LLDB_API_MINOR_VERSION_DOT_51 51 +#define LLDB_API_MINOR_VERSION_DOT_52 52 +#define LLDB_API_MINOR_VERSION_DOT_53 53 +#define LLDB_API_MINOR_VERSION_DOT_54 54 +#define LLDB_API_MINOR_VERSION_DOT_55 55 +#define LLDB_API_MINOR_VERSION_DOT_56 56 +#define LLDB_API_MINOR_VERSION_DOT_57 57 +#define LLDB_API_MINOR_VERSION_DOT_58 58 +#define LLDB_API_MINOR_VERSION_DOT_59 59 +#define LLDB_API_MINOR_VERSION_DOT_60 60 +#define LLDB_API_MINOR_VERSION_DOT_61 61 +#define LLDB_API_MINOR_VERSION_DOT_62 62 +#define LLDB_API_MINOR_VERSION_DOT_63 63 +#define LLDB_API_MINOR_VERSION_DOT_64 64 +#define LLDB_API_MINOR_VERSION_DOT_65 65 +#define LLDB_API_MINOR_VERSION_DOT_66 66 +#define LLDB_API_MINOR_VERSION_DOT_67 67 +#define LLDB_API_MINOR_VERSION_DOT_68 68 +#define LLDB_API_MINOR_VERSION_DOT_69 69 +#define LLDB_API_MINOR_VERSION_DOT_70 70 +#define LLDB_API_MINOR_VERSION_DOT_71 71 +#define LLDB_API_MINOR_VERSION_DOT_72 72 +#define LLDB_API_MINOR_VERSION_DOT_73 73 +#define LLDB_API_MINOR_VERSION_DOT_74 74 +#define LLDB_API_MINOR_VERSION_DOT_75 75 +#define LLDB_API_MINOR_VERSION_DOT_76 76 +#define LLDB_API_MINOR_VERSION_DOT_77 77 +#define LLDB_API_MINOR_VERSION_DOT_78 78 +#define LLDB_API_MINOR_VERSION_DOT_79 79 +#define LLDB_API_MINOR_VERSION_DOT_80 80 +#define LLDB_API_MINOR_VERSION_DOT_81 81 +#define LLDB_API_MINOR_VERSION_DOT_82 82 +#define LLDB_API_MINOR_VERSION_DOT_83 83 +#define LLDB_API_MINOR_VERSION_DOT_84 84 +#define LLDB_API_MINOR_VERSION_DOT_85 85 +#define LLDB_API_MINOR_VERSION_DOT_86 86 +#define LLDB_API_MINOR_VERSION_DOT_87 87 +#define LLDB_API_MINOR_VERSION_DOT_88 88 +#define LLDB_API_MINOR_VERSION_DOT_89 89 +#define LLDB_API_MINOR_VERSION_DOT_90 90 +#define LLDB_API_MINOR_VERSION_DOT_91 91 +#define LLDB_API_MINOR_VERSION_DOT_92 92 +#define LLDB_API_MINOR_VERSION_DOT_93 93 +#define LLDB_API_MINOR_VERSION_DOT_94 94 +#define LLDB_API_MINOR_VERSION_DOT_95 95 +#define LLDB_API_MINOR_VERSION_DOT_96 96 +#define LLDB_API_MINOR_VERSION_DOT_97 97 +#define LLDB_API_MINOR_VERSION_DOT_98 98 +#define LLDB_API_MINOR_VERSION_DOT_99 99 + +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_0 +#define LLDB_API_NEW_IN_DOT_0 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_0 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_0 +#define LLDB_API_DEPRECATED_IN_DOT_0 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_0 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_1 +#define LLDB_API_NEW_IN_DOT_1 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_1 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_1 +#define LLDB_API_DEPRECATED_IN_DOT_1 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_1 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_2 +#define LLDB_API_NEW_IN_DOT_2 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_2 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_2 +#define LLDB_API_DEPRECATED_IN_DOT_2 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_2 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_3 +#define LLDB_API_NEW_IN_DOT_3 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_3 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_3 +#define LLDB_API_DEPRECATED_IN_DOT_3 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_3 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_4 +#define LLDB_API_NEW_IN_DOT_4 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_4 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_4 +#define LLDB_API_DEPRECATED_IN_DOT_4 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_4 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_5 +#define LLDB_API_NEW_IN_DOT_5 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_5 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_5 +#define LLDB_API_DEPRECATED_IN_DOT_5 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_5 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_6 +#define LLDB_API_NEW_IN_DOT_6 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_6 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_6 +#define LLDB_API_DEPRECATED_IN_DOT_6 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_6 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_7 +#define LLDB_API_NEW_IN_DOT_7 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_7 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_7 +#define LLDB_API_DEPRECATED_IN_DOT_7 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_7 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_8 +#define LLDB_API_NEW_IN_DOT_8 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_8 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_8 +#define LLDB_API_DEPRECATED_IN_DOT_8 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_8 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_9 +#define LLDB_API_NEW_IN_DOT_9 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_9 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_9 +#define LLDB_API_DEPRECATED_IN_DOT_9 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_9 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_10 +#define LLDB_API_NEW_IN_DOT_10 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_10 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_10 +#define LLDB_API_DEPRECATED_IN_DOT_10 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_10 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_11 +#define LLDB_API_NEW_IN_DOT_11 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_11 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_11 +#define LLDB_API_DEPRECATED_IN_DOT_11 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_11 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_12 +#define LLDB_API_NEW_IN_DOT_12 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_12 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_12 +#define LLDB_API_DEPRECATED_IN_DOT_12 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_12 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_13 +#define LLDB_API_NEW_IN_DOT_13 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_13 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_13 +#define LLDB_API_DEPRECATED_IN_DOT_13 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_13 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_14 +#define LLDB_API_NEW_IN_DOT_14 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_14 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_14 +#define LLDB_API_DEPRECATED_IN_DOT_14 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_14 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_15 +#define LLDB_API_NEW_IN_DOT_15 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_15 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_15 +#define LLDB_API_DEPRECATED_IN_DOT_15 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_15 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_16 +#define LLDB_API_NEW_IN_DOT_16 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_16 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_16 +#define LLDB_API_DEPRECATED_IN_DOT_16 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_16 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_17 +#define LLDB_API_NEW_IN_DOT_17 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_17 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_17 +#define LLDB_API_DEPRECATED_IN_DOT_17 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_17 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_18 +#define LLDB_API_NEW_IN_DOT_18 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_18 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_18 +#define LLDB_API_DEPRECATED_IN_DOT_18 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_18 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_19 +#define LLDB_API_NEW_IN_DOT_19 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_19 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_19 +#define LLDB_API_DEPRECATED_IN_DOT_19 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_19 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_20 +#define LLDB_API_NEW_IN_DOT_20 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_20 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_20 +#define LLDB_API_DEPRECATED_IN_DOT_20 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_20 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_21 +#define LLDB_API_NEW_IN_DOT_21 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_21 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_21 +#define LLDB_API_DEPRECATED_IN_DOT_21 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_21 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_22 +#define LLDB_API_NEW_IN_DOT_22 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_22 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_22 +#define LLDB_API_DEPRECATED_IN_DOT_22 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_22 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_23 +#define LLDB_API_NEW_IN_DOT_23 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_23 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_23 +#define LLDB_API_DEPRECATED_IN_DOT_23 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_23 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_24 +#define LLDB_API_NEW_IN_DOT_24 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_24 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_24 +#define LLDB_API_DEPRECATED_IN_DOT_24 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_24 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_25 +#define LLDB_API_NEW_IN_DOT_25 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_25 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_25 +#define LLDB_API_DEPRECATED_IN_DOT_25 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_25 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_26 +#define LLDB_API_NEW_IN_DOT_26 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_26 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_26 +#define LLDB_API_DEPRECATED_IN_DOT_26 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_26 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_27 +#define LLDB_API_NEW_IN_DOT_27 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_27 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_27 +#define LLDB_API_DEPRECATED_IN_DOT_27 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_27 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_28 +#define LLDB_API_NEW_IN_DOT_28 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_28 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_28 +#define LLDB_API_DEPRECATED_IN_DOT_28 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_28 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_29 +#define LLDB_API_NEW_IN_DOT_29 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_29 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_29 +#define LLDB_API_DEPRECATED_IN_DOT_29 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_29 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_30 +#define LLDB_API_NEW_IN_DOT_30 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_30 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_30 +#define LLDB_API_DEPRECATED_IN_DOT_30 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_30 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_31 +#define LLDB_API_NEW_IN_DOT_31 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_31 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_31 +#define LLDB_API_DEPRECATED_IN_DOT_31 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_31 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_32 +#define LLDB_API_NEW_IN_DOT_32 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_32 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_32 +#define LLDB_API_DEPRECATED_IN_DOT_32 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_32 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_33 +#define LLDB_API_NEW_IN_DOT_33 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_33 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_33 +#define LLDB_API_DEPRECATED_IN_DOT_33 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_33 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_34 +#define LLDB_API_NEW_IN_DOT_34 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_34 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_34 +#define LLDB_API_DEPRECATED_IN_DOT_34 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_34 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_35 +#define LLDB_API_NEW_IN_DOT_35 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_35 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_35 +#define LLDB_API_DEPRECATED_IN_DOT_35 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_35 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_36 +#define LLDB_API_NEW_IN_DOT_36 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_36 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_36 +#define LLDB_API_DEPRECATED_IN_DOT_36 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_36 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_37 +#define LLDB_API_NEW_IN_DOT_37 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_37 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_37 +#define LLDB_API_DEPRECATED_IN_DOT_37 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_37 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_38 +#define LLDB_API_NEW_IN_DOT_38 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_38 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_38 +#define LLDB_API_DEPRECATED_IN_DOT_38 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_38 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_39 +#define LLDB_API_NEW_IN_DOT_39 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_39 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_39 +#define LLDB_API_DEPRECATED_IN_DOT_39 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_39 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_40 +#define LLDB_API_NEW_IN_DOT_40 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_40 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_40 +#define LLDB_API_DEPRECATED_IN_DOT_40 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_40 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_41 +#define LLDB_API_NEW_IN_DOT_41 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_41 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_41 +#define LLDB_API_DEPRECATED_IN_DOT_41 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_41 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_42 +#define LLDB_API_NEW_IN_DOT_42 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_42 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_42 +#define LLDB_API_DEPRECATED_IN_DOT_42 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_42 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_43 +#define LLDB_API_NEW_IN_DOT_43 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_43 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_43 +#define LLDB_API_DEPRECATED_IN_DOT_43 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_43 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_44 +#define LLDB_API_NEW_IN_DOT_44 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_44 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_44 +#define LLDB_API_DEPRECATED_IN_DOT_44 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_44 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_45 +#define LLDB_API_NEW_IN_DOT_45 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_45 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_45 +#define LLDB_API_DEPRECATED_IN_DOT_45 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_45 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_46 +#define LLDB_API_NEW_IN_DOT_46 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_46 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_46 +#define LLDB_API_DEPRECATED_IN_DOT_46 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_46 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_47 +#define LLDB_API_NEW_IN_DOT_47 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_47 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_47 +#define LLDB_API_DEPRECATED_IN_DOT_47 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_47 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_48 +#define LLDB_API_NEW_IN_DOT_48 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_48 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_48 +#define LLDB_API_DEPRECATED_IN_DOT_48 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_48 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_49 +#define LLDB_API_NEW_IN_DOT_49 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_49 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_49 +#define LLDB_API_DEPRECATED_IN_DOT_49 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_49 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_50 +#define LLDB_API_NEW_IN_DOT_50 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_50 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_50 +#define LLDB_API_DEPRECATED_IN_DOT_50 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_50 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_51 +#define LLDB_API_NEW_IN_DOT_51 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_51 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_51 +#define LLDB_API_DEPRECATED_IN_DOT_51 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_51 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_52 +#define LLDB_API_NEW_IN_DOT_52 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_52 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_52 +#define LLDB_API_DEPRECATED_IN_DOT_52 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_52 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_53 +#define LLDB_API_NEW_IN_DOT_53 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_53 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_53 +#define LLDB_API_DEPRECATED_IN_DOT_53 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_53 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_54 +#define LLDB_API_NEW_IN_DOT_54 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_54 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_54 +#define LLDB_API_DEPRECATED_IN_DOT_54 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_54 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_55 +#define LLDB_API_NEW_IN_DOT_55 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_55 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_55 +#define LLDB_API_DEPRECATED_IN_DOT_55 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_55 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_56 +#define LLDB_API_NEW_IN_DOT_56 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_56 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_56 +#define LLDB_API_DEPRECATED_IN_DOT_56 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_56 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_57 +#define LLDB_API_NEW_IN_DOT_57 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_57 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_57 +#define LLDB_API_DEPRECATED_IN_DOT_57 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_57 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_58 +#define LLDB_API_NEW_IN_DOT_58 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_58 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_58 +#define LLDB_API_DEPRECATED_IN_DOT_58 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_58 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_59 +#define LLDB_API_NEW_IN_DOT_59 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_59 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_59 +#define LLDB_API_DEPRECATED_IN_DOT_59 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_59 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_60 +#define LLDB_API_NEW_IN_DOT_60 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_60 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_60 +#define LLDB_API_DEPRECATED_IN_DOT_60 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_60 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_61 +#define LLDB_API_NEW_IN_DOT_61 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_61 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_61 +#define LLDB_API_DEPRECATED_IN_DOT_61 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_61 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_62 +#define LLDB_API_NEW_IN_DOT_62 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_62 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_62 +#define LLDB_API_DEPRECATED_IN_DOT_62 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_62 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_63 +#define LLDB_API_NEW_IN_DOT_63 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_63 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_63 +#define LLDB_API_DEPRECATED_IN_DOT_63 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_63 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_64 +#define LLDB_API_NEW_IN_DOT_64 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_64 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_64 +#define LLDB_API_DEPRECATED_IN_DOT_64 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_64 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_65 +#define LLDB_API_NEW_IN_DOT_65 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_65 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_65 +#define LLDB_API_DEPRECATED_IN_DOT_65 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_65 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_66 +#define LLDB_API_NEW_IN_DOT_66 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_66 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_66 +#define LLDB_API_DEPRECATED_IN_DOT_66 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_66 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_67 +#define LLDB_API_NEW_IN_DOT_67 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_67 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_67 +#define LLDB_API_DEPRECATED_IN_DOT_67 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_67 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_68 +#define LLDB_API_NEW_IN_DOT_68 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_68 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_68 +#define LLDB_API_DEPRECATED_IN_DOT_68 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_68 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_69 +#define LLDB_API_NEW_IN_DOT_69 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_69 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_69 +#define LLDB_API_DEPRECATED_IN_DOT_69 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_69 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_70 +#define LLDB_API_NEW_IN_DOT_70 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_70 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_70 +#define LLDB_API_DEPRECATED_IN_DOT_70 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_70 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_71 +#define LLDB_API_NEW_IN_DOT_71 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_71 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_71 +#define LLDB_API_DEPRECATED_IN_DOT_71 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_71 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_72 +#define LLDB_API_NEW_IN_DOT_72 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_72 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_72 +#define LLDB_API_DEPRECATED_IN_DOT_72 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_72 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_73 +#define LLDB_API_NEW_IN_DOT_73 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_73 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_73 +#define LLDB_API_DEPRECATED_IN_DOT_73 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_73 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_74 +#define LLDB_API_NEW_IN_DOT_74 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_74 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_74 +#define LLDB_API_DEPRECATED_IN_DOT_74 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_74 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_75 +#define LLDB_API_NEW_IN_DOT_75 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_75 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_75 +#define LLDB_API_DEPRECATED_IN_DOT_75 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_75 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_76 +#define LLDB_API_NEW_IN_DOT_76 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_76 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_76 +#define LLDB_API_DEPRECATED_IN_DOT_76 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_76 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_77 +#define LLDB_API_NEW_IN_DOT_77 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_77 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_77 +#define LLDB_API_DEPRECATED_IN_DOT_77 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_77 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_78 +#define LLDB_API_NEW_IN_DOT_78 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_78 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_78 +#define LLDB_API_DEPRECATED_IN_DOT_78 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_78 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_79 +#define LLDB_API_NEW_IN_DOT_79 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_79 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_79 +#define LLDB_API_DEPRECATED_IN_DOT_79 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_79 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_80 +#define LLDB_API_NEW_IN_DOT_80 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_80 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_80 +#define LLDB_API_DEPRECATED_IN_DOT_80 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_80 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_81 +#define LLDB_API_NEW_IN_DOT_81 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_81 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_81 +#define LLDB_API_DEPRECATED_IN_DOT_81 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_81 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_82 +#define LLDB_API_NEW_IN_DOT_82 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_82 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_82 +#define LLDB_API_DEPRECATED_IN_DOT_82 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_82 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_83 +#define LLDB_API_NEW_IN_DOT_83 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_83 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_83 +#define LLDB_API_DEPRECATED_IN_DOT_83 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_83 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_84 +#define LLDB_API_NEW_IN_DOT_84 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_84 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_84 +#define LLDB_API_DEPRECATED_IN_DOT_84 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_84 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_85 +#define LLDB_API_NEW_IN_DOT_85 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_85 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_85 +#define LLDB_API_DEPRECATED_IN_DOT_85 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_85 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_86 +#define LLDB_API_NEW_IN_DOT_86 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_86 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_86 +#define LLDB_API_DEPRECATED_IN_DOT_86 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_86 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_87 +#define LLDB_API_NEW_IN_DOT_87 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_87 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_87 +#define LLDB_API_DEPRECATED_IN_DOT_87 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_87 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_88 +#define LLDB_API_NEW_IN_DOT_88 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_88 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_88 +#define LLDB_API_DEPRECATED_IN_DOT_88 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_88 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_89 +#define LLDB_API_NEW_IN_DOT_89 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_89 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_89 +#define LLDB_API_DEPRECATED_IN_DOT_89 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_89 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_90 +#define LLDB_API_NEW_IN_DOT_90 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_90 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_90 +#define LLDB_API_DEPRECATED_IN_DOT_90 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_90 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_91 +#define LLDB_API_NEW_IN_DOT_91 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_91 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_91 +#define LLDB_API_DEPRECATED_IN_DOT_91 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_91 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_92 +#define LLDB_API_NEW_IN_DOT_92 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_92 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_92 +#define LLDB_API_DEPRECATED_IN_DOT_92 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_92 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_93 +#define LLDB_API_NEW_IN_DOT_93 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_93 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_93 +#define LLDB_API_DEPRECATED_IN_DOT_93 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_93 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_94 +#define LLDB_API_NEW_IN_DOT_94 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_94 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_94 +#define LLDB_API_DEPRECATED_IN_DOT_94 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_94 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_95 +#define LLDB_API_NEW_IN_DOT_95 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_95 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_95 +#define LLDB_API_DEPRECATED_IN_DOT_95 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_95 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_96 +#define LLDB_API_NEW_IN_DOT_96 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_96 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_96 +#define LLDB_API_DEPRECATED_IN_DOT_96 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_96 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_97 +#define LLDB_API_NEW_IN_DOT_97 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_97 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_97 +#define LLDB_API_DEPRECATED_IN_DOT_97 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_97 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_98 +#define LLDB_API_NEW_IN_DOT_98 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_98 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_98 +#define LLDB_API_DEPRECATED_IN_DOT_98 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_98 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_99 +#define LLDB_API_NEW_IN_DOT_99 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_99 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_99 +#define LLDB_API_DEPRECATED_IN_DOT_99 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_99 +#endif + +#else // defined(LLDB_CHECK_API_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) && defined (LLDB_API_MAJOR_VERSION) + +#define LLDB_API_NEW_IN_DOT_0 +#define LLDB_API_DEPRECATED_IN_DOT_0 +#define LLDB_API_NEW_IN_DOT_1 +#define LLDB_API_DEPRECATED_IN_DOT_1 +#define LLDB_API_NEW_IN_DOT_2 +#define LLDB_API_DEPRECATED_IN_DOT_2 +#define LLDB_API_NEW_IN_DOT_3 +#define LLDB_API_DEPRECATED_IN_DOT_3 +#define LLDB_API_NEW_IN_DOT_4 +#define LLDB_API_DEPRECATED_IN_DOT_4 +#define LLDB_API_NEW_IN_DOT_5 +#define LLDB_API_DEPRECATED_IN_DOT_5 +#define LLDB_API_NEW_IN_DOT_6 +#define LLDB_API_DEPRECATED_IN_DOT_6 +#define LLDB_API_NEW_IN_DOT_7 +#define LLDB_API_DEPRECATED_IN_DOT_7 +#define LLDB_API_NEW_IN_DOT_8 +#define LLDB_API_DEPRECATED_IN_DOT_8 +#define LLDB_API_NEW_IN_DOT_9 +#define LLDB_API_DEPRECATED_IN_DOT_9 +#define LLDB_API_NEW_IN_DOT_10 +#define LLDB_API_DEPRECATED_IN_DOT_10 +#define LLDB_API_NEW_IN_DOT_11 +#define LLDB_API_DEPRECATED_IN_DOT_11 +#define LLDB_API_NEW_IN_DOT_12 +#define LLDB_API_DEPRECATED_IN_DOT_12 +#define LLDB_API_NEW_IN_DOT_13 +#define LLDB_API_DEPRECATED_IN_DOT_13 +#define LLDB_API_NEW_IN_DOT_14 +#define LLDB_API_DEPRECATED_IN_DOT_14 +#define LLDB_API_NEW_IN_DOT_15 +#define LLDB_API_DEPRECATED_IN_DOT_15 +#define LLDB_API_NEW_IN_DOT_16 +#define LLDB_API_DEPRECATED_IN_DOT_16 +#define LLDB_API_NEW_IN_DOT_17 +#define LLDB_API_DEPRECATED_IN_DOT_17 +#define LLDB_API_NEW_IN_DOT_18 +#define LLDB_API_DEPRECATED_IN_DOT_18 +#define LLDB_API_NEW_IN_DOT_19 +#define LLDB_API_DEPRECATED_IN_DOT_19 +#define LLDB_API_NEW_IN_DOT_20 +#define LLDB_API_DEPRECATED_IN_DOT_20 +#define LLDB_API_NEW_IN_DOT_21 +#define LLDB_API_DEPRECATED_IN_DOT_21 +#define LLDB_API_NEW_IN_DOT_22 +#define LLDB_API_DEPRECATED_IN_DOT_22 +#define LLDB_API_NEW_IN_DOT_23 +#define LLDB_API_DEPRECATED_IN_DOT_23 +#define LLDB_API_NEW_IN_DOT_24 +#define LLDB_API_DEPRECATED_IN_DOT_24 +#define LLDB_API_NEW_IN_DOT_25 +#define LLDB_API_DEPRECATED_IN_DOT_25 +#define LLDB_API_NEW_IN_DOT_26 +#define LLDB_API_DEPRECATED_IN_DOT_26 +#define LLDB_API_NEW_IN_DOT_27 +#define LLDB_API_DEPRECATED_IN_DOT_27 +#define LLDB_API_NEW_IN_DOT_28 +#define LLDB_API_DEPRECATED_IN_DOT_28 +#define LLDB_API_NEW_IN_DOT_29 +#define LLDB_API_DEPRECATED_IN_DOT_29 +#define LLDB_API_NEW_IN_DOT_30 +#define LLDB_API_DEPRECATED_IN_DOT_30 +#define LLDB_API_NEW_IN_DOT_31 +#define LLDB_API_DEPRECATED_IN_DOT_31 +#define LLDB_API_NEW_IN_DOT_32 +#define LLDB_API_DEPRECATED_IN_DOT_32 +#define LLDB_API_NEW_IN_DOT_33 +#define LLDB_API_DEPRECATED_IN_DOT_33 +#define LLDB_API_NEW_IN_DOT_34 +#define LLDB_API_DEPRECATED_IN_DOT_34 +#define LLDB_API_NEW_IN_DOT_35 +#define LLDB_API_DEPRECATED_IN_DOT_35 +#define LLDB_API_NEW_IN_DOT_36 +#define LLDB_API_DEPRECATED_IN_DOT_36 +#define LLDB_API_NEW_IN_DOT_37 +#define LLDB_API_DEPRECATED_IN_DOT_37 +#define LLDB_API_NEW_IN_DOT_38 +#define LLDB_API_DEPRECATED_IN_DOT_38 +#define LLDB_API_NEW_IN_DOT_39 +#define LLDB_API_DEPRECATED_IN_DOT_39 +#define LLDB_API_NEW_IN_DOT_40 +#define LLDB_API_DEPRECATED_IN_DOT_40 +#define LLDB_API_NEW_IN_DOT_41 +#define LLDB_API_DEPRECATED_IN_DOT_41 +#define LLDB_API_NEW_IN_DOT_42 +#define LLDB_API_DEPRECATED_IN_DOT_42 +#define LLDB_API_NEW_IN_DOT_43 +#define LLDB_API_DEPRECATED_IN_DOT_43 +#define LLDB_API_NEW_IN_DOT_44 +#define LLDB_API_DEPRECATED_IN_DOT_44 +#define LLDB_API_NEW_IN_DOT_45 +#define LLDB_API_DEPRECATED_IN_DOT_45 +#define LLDB_API_NEW_IN_DOT_46 +#define LLDB_API_DEPRECATED_IN_DOT_46 +#define LLDB_API_NEW_IN_DOT_47 +#define LLDB_API_DEPRECATED_IN_DOT_47 +#define LLDB_API_NEW_IN_DOT_48 +#define LLDB_API_DEPRECATED_IN_DOT_48 +#define LLDB_API_NEW_IN_DOT_49 +#define LLDB_API_DEPRECATED_IN_DOT_49 +#define LLDB_API_NEW_IN_DOT_50 +#define LLDB_API_DEPRECATED_IN_DOT_50 +#define LLDB_API_NEW_IN_DOT_51 +#define LLDB_API_DEPRECATED_IN_DOT_51 +#define LLDB_API_NEW_IN_DOT_52 +#define LLDB_API_DEPRECATED_IN_DOT_52 +#define LLDB_API_NEW_IN_DOT_53 +#define LLDB_API_DEPRECATED_IN_DOT_53 +#define LLDB_API_NEW_IN_DOT_54 +#define LLDB_API_DEPRECATED_IN_DOT_54 +#define LLDB_API_NEW_IN_DOT_55 +#define LLDB_API_DEPRECATED_IN_DOT_55 +#define LLDB_API_NEW_IN_DOT_56 +#define LLDB_API_DEPRECATED_IN_DOT_56 +#define LLDB_API_NEW_IN_DOT_57 +#define LLDB_API_DEPRECATED_IN_DOT_57 +#define LLDB_API_NEW_IN_DOT_58 +#define LLDB_API_DEPRECATED_IN_DOT_58 +#define LLDB_API_NEW_IN_DOT_59 +#define LLDB_API_DEPRECATED_IN_DOT_59 +#define LLDB_API_NEW_IN_DOT_60 +#define LLDB_API_DEPRECATED_IN_DOT_60 +#define LLDB_API_NEW_IN_DOT_61 +#define LLDB_API_DEPRECATED_IN_DOT_61 +#define LLDB_API_NEW_IN_DOT_62 +#define LLDB_API_DEPRECATED_IN_DOT_62 +#define LLDB_API_NEW_IN_DOT_63 +#define LLDB_API_DEPRECATED_IN_DOT_63 +#define LLDB_API_NEW_IN_DOT_64 +#define LLDB_API_DEPRECATED_IN_DOT_64 +#define LLDB_API_NEW_IN_DOT_65 +#define LLDB_API_DEPRECATED_IN_DOT_65 +#define LLDB_API_NEW_IN_DOT_66 +#define LLDB_API_DEPRECATED_IN_DOT_66 +#define LLDB_API_NEW_IN_DOT_67 +#define LLDB_API_DEPRECATED_IN_DOT_67 +#define LLDB_API_NEW_IN_DOT_68 +#define LLDB_API_DEPRECATED_IN_DOT_68 +#define LLDB_API_NEW_IN_DOT_69 +#define LLDB_API_DEPRECATED_IN_DOT_69 +#define LLDB_API_NEW_IN_DOT_70 +#define LLDB_API_DEPRECATED_IN_DOT_70 +#define LLDB_API_NEW_IN_DOT_71 +#define LLDB_API_DEPRECATED_IN_DOT_71 +#define LLDB_API_NEW_IN_DOT_72 +#define LLDB_API_DEPRECATED_IN_DOT_72 +#define LLDB_API_NEW_IN_DOT_73 +#define LLDB_API_DEPRECATED_IN_DOT_73 +#define LLDB_API_NEW_IN_DOT_74 +#define LLDB_API_DEPRECATED_IN_DOT_74 +#define LLDB_API_NEW_IN_DOT_75 +#define LLDB_API_DEPRECATED_IN_DOT_75 +#define LLDB_API_NEW_IN_DOT_76 +#define LLDB_API_DEPRECATED_IN_DOT_76 +#define LLDB_API_NEW_IN_DOT_77 +#define LLDB_API_DEPRECATED_IN_DOT_77 +#define LLDB_API_NEW_IN_DOT_78 +#define LLDB_API_DEPRECATED_IN_DOT_78 +#define LLDB_API_NEW_IN_DOT_79 +#define LLDB_API_DEPRECATED_IN_DOT_79 +#define LLDB_API_NEW_IN_DOT_80 +#define LLDB_API_DEPRECATED_IN_DOT_80 +#define LLDB_API_NEW_IN_DOT_81 +#define LLDB_API_DEPRECATED_IN_DOT_81 +#define LLDB_API_NEW_IN_DOT_82 +#define LLDB_API_DEPRECATED_IN_DOT_82 +#define LLDB_API_NEW_IN_DOT_83 +#define LLDB_API_DEPRECATED_IN_DOT_83 +#define LLDB_API_NEW_IN_DOT_84 +#define LLDB_API_DEPRECATED_IN_DOT_84 +#define LLDB_API_NEW_IN_DOT_85 +#define LLDB_API_DEPRECATED_IN_DOT_85 +#define LLDB_API_NEW_IN_DOT_86 +#define LLDB_API_DEPRECATED_IN_DOT_86 +#define LLDB_API_NEW_IN_DOT_87 +#define LLDB_API_DEPRECATED_IN_DOT_87 +#define LLDB_API_NEW_IN_DOT_88 +#define LLDB_API_DEPRECATED_IN_DOT_88 +#define LLDB_API_NEW_IN_DOT_89 +#define LLDB_API_DEPRECATED_IN_DOT_89 +#define LLDB_API_NEW_IN_DOT_90 +#define LLDB_API_DEPRECATED_IN_DOT_90 +#define LLDB_API_NEW_IN_DOT_91 +#define LLDB_API_DEPRECATED_IN_DOT_91 +#define LLDB_API_NEW_IN_DOT_92 +#define LLDB_API_DEPRECATED_IN_DOT_92 +#define LLDB_API_NEW_IN_DOT_93 +#define LLDB_API_DEPRECATED_IN_DOT_93 +#define LLDB_API_NEW_IN_DOT_94 +#define LLDB_API_DEPRECATED_IN_DOT_94 +#define LLDB_API_NEW_IN_DOT_95 +#define LLDB_API_DEPRECATED_IN_DOT_95 +#define LLDB_API_NEW_IN_DOT_96 +#define LLDB_API_DEPRECATED_IN_DOT_96 +#define LLDB_API_NEW_IN_DOT_97 +#define LLDB_API_DEPRECATED_IN_DOT_97 +#define LLDB_API_NEW_IN_DOT_98 +#define LLDB_API_DEPRECATED_IN_DOT_98 +#define LLDB_API_NEW_IN_DOT_99 +#define LLDB_API_DEPRECATED_IN_DOT_99 +#endif // defined(LLDB_CHECK_API_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) && defined (LLDB_API_MAJOR_VERSION) + +#endif // LLDB_lldb_versioning_h_ \ No newline at end of file diff --git a/source/API/SBAddress.cpp b/source/API/SBAddress.cpp new file mode 100644 index 00000000000..799c9090763 --- /dev/null +++ b/source/API/SBAddress.cpp @@ -0,0 +1,326 @@ +//===-- SBAddress.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBSection.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Target.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBAddress::SBAddress () : + m_opaque_ap () +{ +} + +SBAddress::SBAddress (const Address *lldb_object_ptr) : + m_opaque_ap () +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; +} + +SBAddress::SBAddress (const SBAddress &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + ref() = rhs.ref(); +} + + +SBAddress::SBAddress (lldb::SBSection section, lldb::addr_t offset) : + m_opaque_ap(new Address (section.GetSP(), 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_ap() +{ + SetLoadAddress (load_addr, target); +} + + + +SBAddress::~SBAddress () +{ +} + +const SBAddress & +SBAddress::operator = (const SBAddress &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + ref() = rhs.ref(); + else + m_opaque_ap.reset(); + } + return *this; +} + +bool +SBAddress::IsValid () const +{ + return m_opaque_ap.get() != NULL && m_opaque_ap->IsValid(); +} + +void +SBAddress::Clear () +{ + m_opaque_ap.reset(); +} + +void +SBAddress::SetAddress (lldb::SBSection section, lldb::addr_t 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_ap.reset(); +} + +lldb::addr_t +SBAddress::GetFileAddress () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetFileAddress(); + else + return LLDB_INVALID_ADDRESS; +} + +lldb::addr_t +SBAddress::GetLoadAddress (const SBTarget &target) const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + if (m_opaque_ap.get()) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + addr = m_opaque_ap->GetLoadAddress (target_sp.get()); + } + } + + if (log) + { + if (addr == LLDB_INVALID_ADDRESS) + log->Printf ("SBAddress::GetLoadAddress (SBTarget(%p)) => LLDB_INVALID_ADDRESS", target_sp.get()); + else + log->Printf ("SBAddress::GetLoadAddress (SBTarget(%p)) => 0x%" PRIx64, target_sp.get(), addr); + } + + return addr; +} + +void +SBAddress::SetLoadAddress (lldb::addr_t load_addr, lldb::SBTarget &target) +{ + // Create the address object if we don't already have one + ref(); + if (target.IsValid()) + *this = target.ResolveLoadAddress(load_addr); + else + m_opaque_ap->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_ap->IsValid()) + m_opaque_ap->SetOffset(load_addr); +} + +bool +SBAddress::OffsetAddress (addr_t offset) +{ + if (m_opaque_ap.get()) + { + addr_t addr_offset = m_opaque_ap->GetOffset(); + if (addr_offset != LLDB_INVALID_ADDRESS) + { + m_opaque_ap->SetOffset(addr_offset + offset); + return true; + } + } + return false; +} + +lldb::SBSection +SBAddress::GetSection () +{ + lldb::SBSection sb_section; + if (m_opaque_ap.get()) + sb_section.SetSP (m_opaque_ap->GetSection()); + return sb_section; +} + +lldb::addr_t +SBAddress::GetOffset () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetOffset(); + return 0; +} + +Address * +SBAddress::operator->() +{ + return m_opaque_ap.get(); +} + +const Address * +SBAddress::operator->() const +{ + return m_opaque_ap.get(); +} + +Address & +SBAddress::ref () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new Address()); + return *m_opaque_ap; +} + +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_ap.get()); + return *m_opaque_ap; +} + +Address * +SBAddress::get () +{ + return m_opaque_ap.get(); +} + +bool +SBAddress::GetDescription (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_ap.get()) + { + m_opaque_ap->Dump (&strm, + NULL, + Address::DumpStyleResolvedDescription, + Address::DumpStyleModuleWithFileAddress, + 4); + StreamString sstrm; +// m_opaque_ap->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 () +{ + SBModule sb_module; + if (m_opaque_ap.get()) + sb_module.SetSP (m_opaque_ap->GetModule()); + return sb_module; +} + +SBSymbolContext +SBAddress::GetSymbolContext (uint32_t resolve_scope) +{ + SBSymbolContext sb_sc; + if (m_opaque_ap.get()) + m_opaque_ap->CalculateSymbolContext (&sb_sc.ref(), resolve_scope); + return sb_sc; +} + +SBCompileUnit +SBAddress::GetCompileUnit () +{ + SBCompileUnit sb_comp_unit; + if (m_opaque_ap.get()) + sb_comp_unit.reset(m_opaque_ap->CalculateSymbolContextCompileUnit()); + return sb_comp_unit; +} + +SBFunction +SBAddress::GetFunction () +{ + SBFunction sb_function; + if (m_opaque_ap.get()) + sb_function.reset(m_opaque_ap->CalculateSymbolContextFunction()); + return sb_function; +} + +SBBlock +SBAddress::GetBlock () +{ + SBBlock sb_block; + if (m_opaque_ap.get()) + sb_block.SetPtr(m_opaque_ap->CalculateSymbolContextBlock()); + return sb_block; +} + +SBSymbol +SBAddress::GetSymbol () +{ + SBSymbol sb_symbol; + if (m_opaque_ap.get()) + sb_symbol.reset(m_opaque_ap->CalculateSymbolContextSymbol()); + return sb_symbol; +} + +SBLineEntry +SBAddress::GetLineEntry () +{ + SBLineEntry sb_line_entry; + if (m_opaque_ap.get()) + { + LineEntry line_entry; + if (m_opaque_ap->CalculateSymbolContextLineEntry (line_entry)) + sb_line_entry.SetLineEntry (line_entry); + } + return sb_line_entry; +} + +AddressClass +SBAddress::GetAddressClass () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetAddressClass(); + return eAddressClassInvalid; +} + diff --git a/source/API/SBBlock.cpp b/source/API/SBBlock.cpp new file mode 100644 index 00000000000..c8a665f7d6f --- /dev/null +++ b/source/API/SBBlock.cpp @@ -0,0 +1,373 @@ +//===-- SBBlock.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBBlock.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/Log.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 (NULL) +{ +} + +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) +{ +} + +const SBBlock & +SBBlock::operator = (const SBBlock &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBBlock::~SBBlock () +{ + m_opaque_ptr = NULL; +} + +bool +SBBlock::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +bool +SBBlock::IsInlined () const +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetInlinedFunctionInfo () != NULL; + return false; +} + +const char * +SBBlock::GetInlinedName () const +{ + if (m_opaque_ptr) + { + const InlineFunctionInfo* inlined_info = m_opaque_ptr->GetInlinedFunctionInfo (); + if (inlined_info) + return inlined_info->GetName().AsCString (NULL); + } + return NULL; +} + +SBFileSpec +SBBlock::GetInlinedCallSiteFile () const +{ + 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 sb_file; +} + +uint32_t +SBBlock::GetInlinedCallSiteLine () const +{ + 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 +{ + 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, var_list); + } +} + +SBBlock +SBBlock::GetParent () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetParent(); + return sb_block; +} + +lldb::SBBlock +SBBlock::GetContainingInlinedBlock () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetContainingInlinedBlock (); + return sb_block; +} + +SBBlock +SBBlock::GetSibling () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetSibling(); + return sb_block; +} + +SBBlock +SBBlock::GetFirstChild () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.m_opaque_ptr = m_opaque_ptr->GetFirstChild(); + return 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) +{ + 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 () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetNumRanges(); + return 0; +} + +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 sb_addr; +} + +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 sb_addr; +} + +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) +{ + 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: + 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 value_list; +} + +lldb::SBValueList +SBBlock::GetVariables (lldb::SBTarget& target, + bool arguments, + bool locals, + bool 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: + 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 value_list; +} + diff --git a/source/API/SBBreakpoint.cpp b/source/API/SBBreakpoint.cpp new file mode 100644 index 00000000000..11ad149fddd --- /dev/null +++ b/source/API/SBBreakpoint.cpp @@ -0,0 +1,648 @@ +//===-- SBBreakpoint.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBBreakpoint.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/SBThread.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + + +#include "lldb/lldb-enumerations.h" + +using namespace lldb; +using namespace lldb_private; + +struct CallbackData +{ + SBBreakpoint::BreakpointHitCallback callback; + void *callback_baton; +}; + +class SBBreakpointCallbackBaton : public Baton +{ +public: + + SBBreakpointCallbackBaton (SBBreakpoint::BreakpointHitCallback callback, void *baton) : + Baton (new CallbackData) + { + CallbackData *data = (CallbackData *)m_data; + data->callback = callback; + data->callback_baton = baton; + } + + virtual ~SBBreakpointCallbackBaton() + { + CallbackData *data = (CallbackData *)m_data; + + if (data) + { + delete data; + m_data = NULL; + } + } +}; + + +SBBreakpoint::SBBreakpoint () : + m_opaque_sp () +{ +} + +SBBreakpoint::SBBreakpoint (const SBBreakpoint& rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + + +SBBreakpoint::SBBreakpoint (const lldb::BreakpointSP &bp_sp) : + m_opaque_sp (bp_sp) +{ +} + +SBBreakpoint::~SBBreakpoint() +{ +} + +const SBBreakpoint & +SBBreakpoint::operator = (const SBBreakpoint& rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +bool +SBBreakpoint::operator == (const lldb::SBBreakpoint& rhs) +{ + if (m_opaque_sp && rhs.m_opaque_sp) + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); + return false; +} + +bool +SBBreakpoint::operator != (const lldb::SBBreakpoint& rhs) +{ + if (m_opaque_sp && rhs.m_opaque_sp) + return m_opaque_sp.get() != rhs.m_opaque_sp.get(); + return (m_opaque_sp && !rhs.m_opaque_sp) || (rhs.m_opaque_sp && !m_opaque_sp); +} + +break_id_t +SBBreakpoint::GetID () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + break_id_t break_id = LLDB_INVALID_BREAK_ID; + if (m_opaque_sp) + break_id = m_opaque_sp->GetID(); + + if (log) + { + if (break_id == LLDB_INVALID_BREAK_ID) + log->Printf ("SBBreakpoint(%p)::GetID () => LLDB_INVALID_BREAK_ID", m_opaque_sp.get()); + else + log->Printf ("SBBreakpoint(%p)::GetID () => %u", m_opaque_sp.get(), break_id); + } + + return break_id; +} + + +bool +SBBreakpoint::IsValid() const +{ + return (bool) m_opaque_sp; +} + +void +SBBreakpoint::ClearAllBreakpointSites () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->ClearAllBreakpointSites (); + } +} + +SBBreakpointLocation +SBBreakpoint::FindLocationByAddress (addr_t vm_addr) +{ + SBBreakpointLocation sb_bp_location; + + if (m_opaque_sp) + { + if (vm_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + Address address; + Target &target = m_opaque_sp->GetTarget(); + if (target.GetSectionLoadList().ResolveLoadAddress (vm_addr, address) == false) + { + address.SetRawAddress (vm_addr); + } + sb_bp_location.SetLocation (m_opaque_sp->FindLocationByAddress (address)); + } + } + return sb_bp_location; +} + +break_id_t +SBBreakpoint::FindLocationIDByAddress (addr_t vm_addr) +{ + break_id_t break_id = LLDB_INVALID_BREAK_ID; + + if (m_opaque_sp && vm_addr != LLDB_INVALID_ADDRESS) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + Address address; + Target &target = m_opaque_sp->GetTarget(); + if (target.GetSectionLoadList().ResolveLoadAddress (vm_addr, address) == false) + { + address.SetRawAddress (vm_addr); + } + break_id = m_opaque_sp->FindLocationIDByAddress (address); + } + + return break_id; +} + +SBBreakpointLocation +SBBreakpoint::FindLocationByID (break_id_t bp_loc_id) +{ + SBBreakpointLocation sb_bp_location; + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + sb_bp_location.SetLocation (m_opaque_sp->FindLocationByID (bp_loc_id)); + } + + return sb_bp_location; +} + +SBBreakpointLocation +SBBreakpoint::GetLocationAtIndex (uint32_t index) +{ + SBBreakpointLocation sb_bp_location; + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + sb_bp_location.SetLocation (m_opaque_sp->GetLocationAtIndex (index)); + } + + return sb_bp_location; +} + +void +SBBreakpoint::SetEnabled (bool enable) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetEnabled (enabled=%i)", m_opaque_sp.get(), enable); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetEnabled (enable); + } +} + +bool +SBBreakpoint::IsEnabled () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->IsEnabled(); + } + else + return false; +} + +void +SBBreakpoint::SetOneShot (bool one_shot) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetOneShot (one_shot=%i)", m_opaque_sp.get(), one_shot); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetOneShot (one_shot); + } +} + +bool +SBBreakpoint::IsOneShot () const +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->IsOneShot(); + } + else + return false; +} + +bool +SBBreakpoint::IsInternal () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->IsInternal(); + } + else + return false; +} + +void +SBBreakpoint::SetIgnoreCount (uint32_t count) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetIgnoreCount (count=%u)", m_opaque_sp.get(), count); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetIgnoreCount (count); + } +} + +void +SBBreakpoint::SetCondition (const char *condition) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetCondition (condition); + } +} + +const char * +SBBreakpoint::GetCondition () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + return m_opaque_sp->GetConditionText (); + } + return NULL; +} + +uint32_t +SBBreakpoint::GetHitCount () const +{ + uint32_t count = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + count = m_opaque_sp->GetHitCount(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetHitCount () => %u", m_opaque_sp.get(), count); + + return count; +} + +uint32_t +SBBreakpoint::GetIgnoreCount () const +{ + uint32_t count = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + count = m_opaque_sp->GetIgnoreCount(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetIgnoreCount () => %u", m_opaque_sp.get(), count); + + return count; +} + +void +SBBreakpoint::SetThreadID (tid_t tid) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadID (tid); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetThreadID (tid=0x%4.4" PRIx64 ")", m_opaque_sp.get(), tid); + +} + +tid_t +SBBreakpoint::GetThreadID () +{ + tid_t tid = LLDB_INVALID_THREAD_ID; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + tid = m_opaque_sp->GetThreadID(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetThreadID () => 0x%4.4" PRIx64, m_opaque_sp.get(), tid); + return tid; +} + +void +SBBreakpoint::SetThreadIndex (uint32_t index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetThreadIndex (%u)", m_opaque_sp.get(), index); + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->GetOptions()->GetThreadSpec()->SetIndex (index); + } +} + +uint32_t +SBBreakpoint::GetThreadIndex() const +{ + uint32_t thread_idx = UINT32_MAX; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + const ThreadSpec *thread_spec = m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); + if (thread_spec != NULL) + thread_idx = thread_spec->GetIndex(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetThreadIndex () => %u", m_opaque_sp.get(), thread_idx); + + return thread_idx; +} + + +void +SBBreakpoint::SetThreadName (const char *thread_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetThreadName (%s)", m_opaque_sp.get(), thread_name); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->GetOptions()->GetThreadSpec()->SetName (thread_name); + } +} + +const char * +SBBreakpoint::GetThreadName () const +{ + const char *name = NULL; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + const ThreadSpec *thread_spec = m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); + if (thread_spec != NULL) + name = thread_spec->GetName(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetThreadName () => %s", m_opaque_sp.get(), name); + + return name; +} + +void +SBBreakpoint::SetQueueName (const char *queue_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::SetQueueName (%s)", m_opaque_sp.get(), queue_name); + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + m_opaque_sp->GetOptions()->GetThreadSpec()->SetQueueName (queue_name); + } +} + +const char * +SBBreakpoint::GetQueueName () const +{ + const char *name = NULL; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + const ThreadSpec *thread_spec = m_opaque_sp->GetOptions()->GetThreadSpecNoCreate(); + if (thread_spec) + name = thread_spec->GetQueueName(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetQueueName () => %s", m_opaque_sp.get(), name); + + return name; +} + +size_t +SBBreakpoint::GetNumResolvedLocations() const +{ + size_t num_resolved = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + num_resolved = m_opaque_sp->GetNumResolvedLocations(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetNumResolvedLocations () => %" PRIu64, m_opaque_sp.get(), (uint64_t)num_resolved); + return num_resolved; +} + +size_t +SBBreakpoint::GetNumLocations() const +{ + size_t num_locs = 0; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + num_locs = m_opaque_sp->GetNumLocations(); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBreakpoint(%p)::GetNumLocations () => %" PRIu64, m_opaque_sp.get(), (uint64_t)num_locs); + return num_locs; +} + +bool +SBBreakpoint::GetDescription (SBStream &s) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + s.Printf("SBBreakpoint: id = %i, ", m_opaque_sp->GetID()); + m_opaque_sp->GetResolverDescription (s.get()); + m_opaque_sp->GetFilterDescription (s.get()); + const size_t num_locations = m_opaque_sp->GetNumLocations (); + s.Printf(", locations = %" PRIu64, (uint64_t)num_locations); + return true; + } + s.Printf ("No value"); + return false; +} + +bool +SBBreakpoint::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 +} + +void +SBBreakpoint::SetCallback (BreakpointHitCallback callback, void *baton) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBreakpoint(%p)::SetCallback (callback=%p, baton=%p)", m_opaque_sp.get(), callback, baton); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetTarget().GetAPIMutex()); + BatonSP baton_sp(new SBBreakpointCallbackBaton (callback, baton)); + m_opaque_sp->SetCallback (SBBreakpoint::PrivateBreakpointHitCallback, baton_sp, false); + } +} + + +lldb_private::Breakpoint * +SBBreakpoint::operator->() const +{ + return m_opaque_sp.get(); +} + +lldb_private::Breakpoint * +SBBreakpoint::get() const +{ + return m_opaque_sp.get(); +} + +lldb::BreakpointSP & +SBBreakpoint::operator *() +{ + return m_opaque_sp; +} + +const lldb::BreakpointSP & +SBBreakpoint::operator *() const +{ + return m_opaque_sp; +} + +bool +SBBreakpoint::EventIsBreakpointEvent (const lldb::SBEvent &event) +{ + return Breakpoint::BreakpointEventData::GetEventDataFromEvent(event.get()) != NULL; + +} + +BreakpointEventType +SBBreakpoint::GetBreakpointEventTypeFromEvent (const SBEvent& event) +{ + if (event.IsValid()) + return Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (event.GetSP()); + return eBreakpointEventTypeInvalidType; +} + +SBBreakpoint +SBBreakpoint::GetBreakpointFromEvent (const lldb::SBEvent& event) +{ + SBBreakpoint sb_breakpoint; + if (event.IsValid()) + sb_breakpoint.m_opaque_sp = Breakpoint::BreakpointEventData::GetBreakpointFromEvent (event.GetSP()); + return sb_breakpoint; +} + +SBBreakpointLocation +SBBreakpoint::GetBreakpointLocationAtIndexFromEvent (const lldb::SBEvent& event, uint32_t loc_idx) +{ + SBBreakpointLocation sb_breakpoint_loc; + if (event.IsValid()) + sb_breakpoint_loc.SetLocation (Breakpoint::BreakpointEventData::GetBreakpointLocationAtIndexFromEvent (event.GetSP(), loc_idx)); + return sb_breakpoint_loc; +} + +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; +} + + diff --git a/source/API/SBBreakpointLocation.cpp b/source/API/SBBreakpointLocation.cpp new file mode 100644 index 00000000000..6fdf59f38b4 --- /dev/null +++ b/source/API/SBBreakpointLocation.cpp @@ -0,0 +1,320 @@ +//===-- SBBreakpointLocation.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBStream.h" + +#include "lldb/lldb-types.h" +#include "lldb/lldb-defines.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" + +using namespace lldb; +using namespace lldb_private; + + +SBBreakpointLocation::SBBreakpointLocation () : + m_opaque_sp () +{ +} + +SBBreakpointLocation::SBBreakpointLocation (const lldb::BreakpointLocationSP &break_loc_sp) : + m_opaque_sp (break_loc_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + GetDescription (sstr, lldb::eDescriptionLevelBrief); + log->Printf ("SBBreakpointLocation::SBBreakpointLocaiton (const lldb::BreakpointLocationsSP &break_loc_sp" + "=%p) => this.sp = %p (%s)", break_loc_sp.get(), m_opaque_sp.get(), sstr.GetData()); + } +} + +SBBreakpointLocation::SBBreakpointLocation(const SBBreakpointLocation &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBBreakpointLocation & +SBBreakpointLocation::operator = (const SBBreakpointLocation &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + + +SBBreakpointLocation::~SBBreakpointLocation () +{ +} + +bool +SBBreakpointLocation::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +SBAddress +SBBreakpointLocation::GetAddress () +{ + if (m_opaque_sp) + return SBAddress(&m_opaque_sp->GetAddress()); + else + return SBAddress(); +} + +addr_t +SBBreakpointLocation::GetLoadAddress () +{ + addr_t ret_addr = LLDB_INVALID_ADDRESS; + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + ret_addr = m_opaque_sp->GetLoadAddress(); + } + + return ret_addr; +} + +void +SBBreakpointLocation::SetEnabled (bool enabled) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetEnabled (enabled); + } +} + +bool +SBBreakpointLocation::IsEnabled () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->IsEnabled(); + } + else + return false; +} + +uint32_t +SBBreakpointLocation::GetIgnoreCount () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetIgnoreCount(); + } + else + return 0; +} + +void +SBBreakpointLocation::SetIgnoreCount (uint32_t n) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetIgnoreCount (n); + } +} + +void +SBBreakpointLocation::SetCondition (const char *condition) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetCondition (condition); + } +} + +const char * +SBBreakpointLocation::GetCondition () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetConditionText (); + } + return NULL; +} + +void +SBBreakpointLocation::SetThreadID (tid_t thread_id) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadID (thread_id); + } +} + +tid_t +SBBreakpointLocation::GetThreadID () +{ + tid_t tid = LLDB_INVALID_THREAD_ID; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetThreadID(); + } + return tid; +} + +void +SBBreakpointLocation::SetThreadIndex (uint32_t index) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadIndex (index); + } +} + +uint32_t +SBBreakpointLocation::GetThreadIndex() const +{ + uint32_t thread_idx = UINT32_MAX; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetThreadIndex(); + } + return thread_idx; +} + + +void +SBBreakpointLocation::SetThreadName (const char *thread_name) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetThreadName (thread_name); + } +} + +const char * +SBBreakpointLocation::GetThreadName () const +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetThreadName(); + } + return NULL; +} + +void +SBBreakpointLocation::SetQueueName (const char *queue_name) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->SetQueueName (queue_name); + } +} + +const char * +SBBreakpointLocation::GetQueueName () const +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->GetQueueName (); + } + return NULL; +} + +bool +SBBreakpointLocation::IsResolved () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->IsResolved(); + } + return false; +} + +void +SBBreakpointLocation::SetLocation (const lldb::BreakpointLocationSP &break_loc_sp) +{ + // Uninstall the callbacks? + m_opaque_sp = break_loc_sp; +} + +bool +SBBreakpointLocation::GetDescription (SBStream &description, DescriptionLevel level) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + m_opaque_sp->GetDescription (&strm, level); + strm.EOL(); + } + else + strm.PutCString ("No value"); + + return true; +} + +break_id_t +SBBreakpointLocation::GetID () +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + return m_opaque_sp->GetID (); + } + else + return LLDB_INVALID_BREAK_ID; +} + +SBBreakpoint +SBBreakpointLocation::GetBreakpoint () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + //if (log) + // log->Printf ("SBBreakpointLocation::GetBreakpoint ()"); + + SBBreakpoint sb_bp; + if (m_opaque_sp) + { + Mutex::Locker api_locker (m_opaque_sp->GetBreakpoint().GetTarget().GetAPIMutex()); + *sb_bp = m_opaque_sp->GetBreakpoint ().shared_from_this(); + } + + if (log) + { + SBStream sstr; + sb_bp.GetDescription (sstr); + log->Printf ("SBBreakpointLocation(%p)::GetBreakpoint () => SBBreakpoint(%p) %s", + m_opaque_sp.get(), sb_bp.get(), sstr.GetData()); + } + return sb_bp; +} + diff --git a/source/API/SBBroadcaster.cpp b/source/API/SBBroadcaster.cpp new file mode 100644 index 00000000000..7168305ac80 --- /dev/null +++ b/source/API/SBBroadcaster.cpp @@ -0,0 +1,196 @@ +//===-- SBBroadcaster.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Log.h" + +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBEvent.h" + +using namespace lldb; +using namespace lldb_private; + + +SBBroadcaster::SBBroadcaster () : + m_opaque_sp (), + m_opaque_ptr (NULL) +{ +} + +SBBroadcaster::SBBroadcaster (const char *name) : + m_opaque_sp (new Broadcaster (NULL, name)), + m_opaque_ptr (NULL) +{ + m_opaque_ptr = m_opaque_sp.get(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API | LIBLLDB_LOG_VERBOSE)); + + if (log) + log->Printf ("SBBroadcaster::SBBroadcaster (name=\"%s\") => SBBroadcaster(%p)", + name, m_opaque_ptr); +} + +SBBroadcaster::SBBroadcaster (lldb_private::Broadcaster *broadcaster, bool owns) : + m_opaque_sp (owns ? broadcaster : NULL), + m_opaque_ptr (broadcaster) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API | LIBLLDB_LOG_VERBOSE)); + + if (log) + log->Printf ("SBBroadcaster::SBBroadcaster (broadcaster=%p, bool owns=%i) => SBBroadcaster(%p)", + broadcaster, owns, m_opaque_ptr); +} + +SBBroadcaster::SBBroadcaster (const SBBroadcaster &rhs) : + m_opaque_sp (rhs.m_opaque_sp), + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBBroadcaster & +SBBroadcaster::operator = (const SBBroadcaster &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + m_opaque_ptr = rhs.m_opaque_ptr; + } + return *this; +} + +SBBroadcaster::~SBBroadcaster() +{ + reset (NULL, false); +} + +void +SBBroadcaster::BroadcastEventByType (uint32_t event_type, bool unique) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBroadcaster(%p)::BroadcastEventByType (event_type=0x%8.8x, unique=%i)", m_opaque_ptr, event_type, unique); + + if (m_opaque_ptr == NULL) + return; + + if (unique) + m_opaque_ptr->BroadcastEventIfUnique (event_type); + else + m_opaque_ptr->BroadcastEvent (event_type); +} + +void +SBBroadcaster::BroadcastEvent (const SBEvent &event, bool unique) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBBroadcaster(%p)::BroadcastEventByType (SBEvent(%p), unique=%i)", m_opaque_ptr, event.get(), unique); + + if (m_opaque_ptr == NULL) + 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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBBroadcaster(%p)::AddInitialEventsToListener (SBListener(%p), event_mask=0x%8.8x)", m_opaque_ptr, listener.get(), requested_events); + if (m_opaque_ptr) + m_opaque_ptr->AddInitialEventsToListener (listener.get(), requested_events); +} + +uint32_t +SBBroadcaster::AddListener (const SBListener &listener, uint32_t event_mask) +{ + if (m_opaque_ptr) + return m_opaque_ptr->AddListener (listener.get(), event_mask); + return 0; +} + +const char * +SBBroadcaster::GetName () const +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetBroadcasterName().GetCString(); + return NULL; +} + +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) +{ + if (m_opaque_ptr) + return m_opaque_ptr->RemoveListener (listener.get(), 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 +{ + return m_opaque_ptr != NULL; +} + +void +SBBroadcaster::Clear () +{ + m_opaque_sp.reset(); + m_opaque_ptr = NULL; +} + +bool +SBBroadcaster::operator == (const SBBroadcaster &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; + +} + +bool +SBBroadcaster::operator != (const SBBroadcaster &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +bool +SBBroadcaster::operator < (const SBBroadcaster &rhs) const +{ + return m_opaque_ptr < rhs.m_opaque_ptr; +} diff --git a/source/API/SBCommandInterpreter.cpp b/source/API/SBCommandInterpreter.cpp new file mode 100644 index 00000000000..0c839004601 --- /dev/null +++ b/source/API/SBCommandInterpreter.cpp @@ -0,0 +1,490 @@ +//===-- SBCommandInterpreter.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/lldb-types.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/Listener.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" + +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" + +using namespace lldb; +using namespace lldb_private; + +class CommandPluginInterfaceImplementation : public CommandObjectParsed +{ +public: + CommandPluginInterfaceImplementation (CommandInterpreter &interpreter, + const char *name, + lldb::SBCommandPluginInterface* backend, + const char *help = NULL, + const char *syntax = NULL, + uint32_t flags = 0) : + CommandObjectParsed (interpreter, name, help, syntax, flags), + m_backend(backend) {} + + virtual bool + IsRemovable() const { return true; } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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; + } + lldb::SBCommandPluginInterface* m_backend; +}; + +SBCommandInterpreter::SBCommandInterpreter (CommandInterpreter *interpreter) : + m_opaque_ptr (interpreter) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter::SBCommandInterpreter (interpreter=%p)" + " => SBCommandInterpreter(%p)", interpreter, m_opaque_ptr); +} + +SBCommandInterpreter::SBCommandInterpreter(const SBCommandInterpreter &rhs) : + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const SBCommandInterpreter & +SBCommandInterpreter::operator = (const SBCommandInterpreter &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBCommandInterpreter::~SBCommandInterpreter () +{ +} + +bool +SBCommandInterpreter::IsValid() const +{ + return m_opaque_ptr != NULL; +} + + +bool +SBCommandInterpreter::CommandExists (const char *cmd) +{ + if (cmd && m_opaque_ptr) + return m_opaque_ptr->CommandExists (cmd); + return false; +} + +bool +SBCommandInterpreter::AliasExists (const char *cmd) +{ + if (cmd && m_opaque_ptr) + return m_opaque_ptr->AliasExists (cmd); + return false; +} + +lldb::ReturnStatus +SBCommandInterpreter::HandleCommand (const char *command_line, SBCommandReturnObject &result, bool add_to_history) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::HandleCommand (command=\"%s\", SBCommandReturnObject(%p), add_to_history=%i)", + m_opaque_ptr, command_line, result.get(), add_to_history); + + result.Clear(); + if (command_line && m_opaque_ptr) + { + m_opaque_ptr->HandleCommand (command_line, add_to_history ? eLazyBoolYes : eLazyBoolNo, result.ref()); + } + else + { + result->AppendError ("SBCommandInterpreter or the command line is not valid"); + result->SetStatus (eReturnStatusFailed); + } + + // We need to get the value again, in case the command disabled the log! + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + SBStream sstr; + result.GetDescription (sstr); + log->Printf ("SBCommandInterpreter(%p)::HandleCommand (command=\"%s\", SBCommandReturnObject(%p): %s, add_to_history=%i) => %i", + m_opaque_ptr, command_line, result.get(), sstr.GetData(), add_to_history, result.GetStatus()); + } + + return result.GetStatus(); +} + +int +SBCommandInterpreter::HandleCompletion (const char *current_line, + const char *cursor, + const char *last_char, + int match_start_point, + int max_return_elements, + SBStringList &matches) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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 == NULL || cursor == NULL || last_char == NULL) + return 0; + + if (cursor < current_line || last_char < current_line) + return 0; + + size_t current_line_size = strlen (current_line); + if (cursor - current_line > current_line_size || last_char - current_line > current_line_size) + return 0; + + if (log) + log->Printf ("SBCommandInterpreter(%p)::HandleCompletion (current_line=\"%s\", cursor at: %" PRId64 ", last char at: %" PRId64 ", match_start_point: %d, max_return_elements: %d)", + m_opaque_ptr, current_line, (uint64_t) (cursor - current_line), (uint64_t) (last_char - current_line), match_start_point, max_return_elements); + + if (m_opaque_ptr) + { + lldb_private::StringList lldb_matches; + num_completions = m_opaque_ptr->HandleCompletion (current_line, cursor, last_char, match_start_point, + max_return_elements, lldb_matches); + + SBStringList temp_list (&lldb_matches); + matches.AppendList (temp_list); + } + if (log) + log->Printf ("SBCommandInterpreter(%p)::HandleCompletion - Found %d completions.", m_opaque_ptr, num_completions); + + return num_completions; +} + +int +SBCommandInterpreter::HandleCompletion (const char *current_line, + uint32_t cursor_pos, + int match_start_point, + int max_return_elements, + lldb::SBStringList &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 () +{ + if (m_opaque_ptr) + return m_opaque_ptr->HasCommands(); + return false; +} + +bool +SBCommandInterpreter::HasAliases () +{ + if (m_opaque_ptr) + return m_opaque_ptr->HasAliases(); + return false; +} + +bool +SBCommandInterpreter::HasAliasOptions () +{ + if (m_opaque_ptr) + return m_opaque_ptr->HasAliasOptions (); + return false; +} + +SBProcess +SBCommandInterpreter::GetProcess () +{ + SBProcess sb_process; + ProcessSP process_sp; + if (m_opaque_ptr) + { + TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); + if (target_sp) + { + Mutex::Locker api_locker(target_sp->GetAPIMutex()); + process_sp = target_sp->GetProcessSP(); + sb_process.SetSP(process_sp); + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::GetProcess () => SBProcess(%p)", + m_opaque_ptr, process_sp.get()); + + + return sb_process; +} + +SBDebugger +SBCommandInterpreter::GetDebugger () +{ + SBDebugger sb_debugger; + if (m_opaque_ptr) + sb_debugger.reset(m_opaque_ptr->GetDebugger().shared_from_this()); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::GetDebugger () => SBDebugger(%p)", + m_opaque_ptr, sb_debugger.get()); + + + return sb_debugger; +} + +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) +{ + result.Clear(); + if (m_opaque_ptr) + { + TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + m_opaque_ptr->SourceInitFile (false, result.ref()); + } + else + { + result->AppendError ("SBCommandInterpreter is not valid"); + result->SetStatus (eReturnStatusFailed); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::SourceInitFileInHomeDirectory (&SBCommandReturnObject(%p))", + m_opaque_ptr, result.get()); + +} + +void +SBCommandInterpreter::SourceInitFileInCurrentWorkingDirectory (SBCommandReturnObject &result) +{ + result.Clear(); + if (m_opaque_ptr) + { + TargetSP target_sp(m_opaque_ptr->GetDebugger().GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + m_opaque_ptr->SourceInitFile (true, result.ref()); + } + else + { + result->AppendError ("SBCommandInterpreter is not valid"); + result->SetStatus (eReturnStatusFailed); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::SourceInitFileInCurrentWorkingDirectory (&SBCommandReturnObject(%p))", + m_opaque_ptr, result.get()); +} + +SBBroadcaster +SBCommandInterpreter::GetBroadcaster () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBroadcaster broadcaster (m_opaque_ptr, false); + + if (log) + log->Printf ("SBCommandInterpreter(%p)::GetBroadcaster() => SBBroadcaster(%p)", + m_opaque_ptr, broadcaster.get()); + + return broadcaster; +} + +const char * +SBCommandInterpreter::GetBroadcasterClass () +{ + return Communication::GetStaticBroadcasterClass().AsCString(); +} + +const char * +SBCommandInterpreter::GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type) +{ + return CommandObject::GetArgumentTypeAsCString (arg_type); +} + +const char * +SBCommandInterpreter::GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type) +{ + return CommandObject::GetArgumentDescriptionAsCString (arg_type); +} + +bool +SBCommandInterpreter::SetCommandOverrideCallback (const char *command_name, + lldb::CommandOverrideCallback callback, + void *baton) +{ + if (command_name && command_name[0] && m_opaque_ptr) + { + std::string 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; +} + +#ifndef LLDB_DISABLE_PYTHON + +// Defined in the SWIG source file +extern "C" void +init_lldb(void); + +#else + +extern "C" void init_lldb(void); + +// Usually defined in the SWIG source file, but we have sripting disabled +extern "C" void +init_lldb(void) +{ +} + +#endif + +void +SBCommandInterpreter::InitializeSWIG () +{ + static bool g_initialized = false; + if (!g_initialized) + { + g_initialized = true; +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreter::InitializeInterpreter (init_lldb); +#endif + } +} + +lldb::SBCommand +SBCommandInterpreter::AddMultiwordCommand (const char* name, const char* 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::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + +lldb::SBCommand +SBCommandInterpreter::AddCommand (const char* name, lldb::SBCommandPluginInterface* impl, const char* help) +{ + lldb::CommandObjectSP new_command_sp; + new_command_sp.reset(new CommandPluginInterfaceImplementation(*m_opaque_ptr,name,impl,help)); + + if (new_command_sp && m_opaque_ptr->AddUserCommand(name, new_command_sp, true)) + return lldb::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + +SBCommand::SBCommand () +{} + +SBCommand::SBCommand (lldb::CommandObjectSP cmd_sp) : m_opaque_sp (cmd_sp) +{} + +bool +SBCommand::IsValid () +{ + return (bool)m_opaque_sp; +} + +const char* +SBCommand::GetName () +{ + if (IsValid ()) + return m_opaque_sp->GetCommandName (); + return NULL; +} + +const char* +SBCommand::GetHelp () +{ + if (IsValid ()) + return m_opaque_sp->GetHelp (); + return NULL; +} + +lldb::SBCommand +SBCommand::AddMultiwordCommand (const char* name, const char* help) +{ + if (!IsValid ()) + return lldb::SBCommand(); + if (m_opaque_sp->IsMultiwordObject() == false) + return 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::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + +lldb::SBCommand +SBCommand::AddCommand (const char* name, lldb::SBCommandPluginInterface *impl, const char* help) +{ + if (!IsValid ()) + return lldb::SBCommand(); + if (m_opaque_sp->IsMultiwordObject() == false) + return lldb::SBCommand(); + lldb::CommandObjectSP new_command_sp; + new_command_sp.reset(new CommandPluginInterfaceImplementation(m_opaque_sp->GetCommandInterpreter(),name,impl,help)); + if (new_command_sp && m_opaque_sp->LoadSubCommand(name,new_command_sp)) + return lldb::SBCommand(new_command_sp); + return lldb::SBCommand(); +} + diff --git a/source/API/SBCommandReturnObject.cpp b/source/API/SBCommandReturnObject.cpp new file mode 100644 index 00000000000..83d65637d3f --- /dev/null +++ b/source/API/SBCommandReturnObject.cpp @@ -0,0 +1,352 @@ +//===-- SBCommandReturnObject.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +SBCommandReturnObject::SBCommandReturnObject () : + m_opaque_ap (new CommandReturnObject ()) +{ +} + +SBCommandReturnObject::SBCommandReturnObject (const SBCommandReturnObject &rhs): + m_opaque_ap () +{ + if (rhs.m_opaque_ap.get()) + m_opaque_ap.reset (new CommandReturnObject (*rhs.m_opaque_ap)); +} + +SBCommandReturnObject::SBCommandReturnObject (CommandReturnObject *ptr) : + m_opaque_ap (ptr) +{ +} + +CommandReturnObject * +SBCommandReturnObject::Release () +{ + return m_opaque_ap.release(); +} + +const SBCommandReturnObject & +SBCommandReturnObject::operator = (const SBCommandReturnObject &rhs) +{ + if (this != &rhs) + { + if (rhs.m_opaque_ap.get()) + m_opaque_ap.reset (new CommandReturnObject (*rhs.m_opaque_ap)); + else + m_opaque_ap.reset(); + } + return *this; +} + + +SBCommandReturnObject::~SBCommandReturnObject () +{ + // m_opaque_ap will automatically delete any pointer it owns +} + +bool +SBCommandReturnObject::IsValid() const +{ + return m_opaque_ap.get() != NULL; +} + + +const char * +SBCommandReturnObject::GetOutput () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (m_opaque_ap.get()) + { + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetOutput () => \"%s\"", m_opaque_ap.get(), + m_opaque_ap->GetOutputData()); + + return m_opaque_ap->GetOutputData(); + } + + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetOutput () => NULL", m_opaque_ap.get()); + + return NULL; +} + +const char * +SBCommandReturnObject::GetError () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (m_opaque_ap.get()) + { + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetError () => \"%s\"", m_opaque_ap.get(), + m_opaque_ap->GetErrorData()); + + return m_opaque_ap->GetErrorData(); + } + + if (log) + log->Printf ("SBCommandReturnObject(%p)::GetError () => NULL", m_opaque_ap.get()); + + return NULL; +} + +size_t +SBCommandReturnObject::GetOutputSize () +{ + if (m_opaque_ap.get()) + return strlen (m_opaque_ap->GetOutputData()); + return 0; +} + +size_t +SBCommandReturnObject::GetErrorSize () +{ + if (m_opaque_ap.get()) + return strlen(m_opaque_ap->GetErrorData()); + return 0; +} + +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) +{ + if (fh) + { + size_t num_bytes = GetErrorSize (); + if (num_bytes) + return ::fprintf (fh, "%s", GetError()); + } + return 0; +} + +void +SBCommandReturnObject::Clear() +{ + if (m_opaque_ap.get()) + m_opaque_ap->Clear(); +} + +lldb::ReturnStatus +SBCommandReturnObject::GetStatus() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetStatus(); + return lldb::eReturnStatusInvalid; +} + +void +SBCommandReturnObject::SetStatus(lldb::ReturnStatus status) +{ + if (m_opaque_ap.get()) + m_opaque_ap->SetStatus(status); +} + +bool +SBCommandReturnObject::Succeeded () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->Succeeded(); + return false; +} + +bool +SBCommandReturnObject::HasResult () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->HasResult(); + return false; +} + +void +SBCommandReturnObject::AppendMessage (const char *message) +{ + if (m_opaque_ap.get()) + m_opaque_ap->AppendMessage (message); +} + +void +SBCommandReturnObject::AppendWarning (const char *message) +{ + if (m_opaque_ap.get()) + m_opaque_ap->AppendWarning (message); +} + +CommandReturnObject * +SBCommandReturnObject::operator ->() const +{ + return m_opaque_ap.get(); +} + +CommandReturnObject * +SBCommandReturnObject::get() const +{ + return m_opaque_ap.get(); +} + +CommandReturnObject & +SBCommandReturnObject::operator *() const +{ + assert(m_opaque_ap.get()); + return *(m_opaque_ap.get()); +} + + +CommandReturnObject & +SBCommandReturnObject::ref() const +{ + assert(m_opaque_ap.get()); + return *(m_opaque_ap.get()); +} + + +void +SBCommandReturnObject::SetLLDBObjectPtr (CommandReturnObject *ptr) +{ + if (m_opaque_ap.get()) + m_opaque_ap.reset (ptr); +} + +bool +SBCommandReturnObject::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + description.Printf ("Status: "); + lldb::ReturnStatus status = m_opaque_ap->GetStatus(); + if (status == lldb::eReturnStatusStarted) + strm.PutCString ("Started"); + else if (status == lldb::eReturnStatusInvalid) + strm.PutCString ("Invalid"); + else if (m_opaque_ap->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) +{ + if (m_opaque_ap.get()) + m_opaque_ap->SetImmediateOutputFile (fh); +} + +void +SBCommandReturnObject::SetImmediateErrorFile (FILE *fh) +{ + if (m_opaque_ap.get()) + m_opaque_ap->SetImmediateErrorFile (fh); +} + +void +SBCommandReturnObject::PutCString(const char* string, int len) +{ + if (m_opaque_ap.get()) + { + if (len == 0 || string == NULL || *string == 0) + { + return; + } + else if (len > 0) + { + std::string buffer(string, len); + m_opaque_ap->AppendMessage(buffer.c_str()); + } + else + m_opaque_ap->AppendMessage(string); + } +} + +const char * +SBCommandReturnObject::GetOutput (bool only_if_no_immediate) +{ + if (!m_opaque_ap.get()) + return NULL; + if (only_if_no_immediate == false || m_opaque_ap->GetImmediateOutputStream().get() == NULL) + return GetOutput(); + return NULL; +} + +const char * +SBCommandReturnObject::GetError (bool only_if_no_immediate) +{ + if (!m_opaque_ap.get()) + return NULL; + if (only_if_no_immediate == false || m_opaque_ap->GetImmediateErrorStream().get() == NULL) + return GetError(); + return NULL; +} + +size_t +SBCommandReturnObject::Printf(const char* format, ...) +{ + if (m_opaque_ap.get()) + { + va_list args; + va_start (args, format); + size_t result = m_opaque_ap->GetOutputStream().PrintfVarArg(format, args); + va_end (args); + return result; + } + return 0; +} + +void +SBCommandReturnObject::SetError (lldb::SBError &error, const char *fallback_error_cstr) +{ + if (m_opaque_ap.get()) + { + if (error.IsValid()) + m_opaque_ap->SetError(error.ref(), fallback_error_cstr); + else if (fallback_error_cstr) + m_opaque_ap->SetError(Error(), fallback_error_cstr); + } +} + +void +SBCommandReturnObject::SetError (const char *error_cstr) +{ + if (m_opaque_ap.get() && error_cstr) + m_opaque_ap->SetError(error_cstr); +} + diff --git a/source/API/SBCommunication.cpp b/source/API/SBCommunication.cpp new file mode 100644 index 00000000000..10feae5d4eb --- /dev/null +++ b/source/API/SBCommunication.cpp @@ -0,0 +1,285 @@ +//===-- SBCommunication.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBCommunication.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBCommunication::SBCommunication() : + m_opaque (NULL), + m_opaque_owned (false) +{ +} + +SBCommunication::SBCommunication(const char * broadcaster_name) : + m_opaque (new Communication (broadcaster_name)), + m_opaque_owned (true) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommunication::SBCommunication (broadcaster_name=\"%s\") => " + "SBCommunication(%p)", broadcaster_name, m_opaque); +} + +SBCommunication::~SBCommunication() +{ + if (m_opaque && m_opaque_owned) + delete m_opaque; + m_opaque = NULL; + m_opaque_owned = false; +} + +bool +SBCommunication::IsValid () const +{ + return m_opaque != NULL; +} + +bool +SBCommunication::GetCloseOnEOF () +{ + if (m_opaque) + return m_opaque->GetCloseOnEOF (); + return false; +} + +void +SBCommunication::SetCloseOnEOF (bool b) +{ + if (m_opaque) + m_opaque->SetCloseOnEOF (b); +} + +ConnectionStatus +SBCommunication::Connect (const char *url) +{ + if (m_opaque) + { + if (!m_opaque->HasConnection ()) + m_opaque->SetConnection (new ConnectionFileDescriptor()); + return m_opaque->Connect (url, NULL); + } + return eConnectionStatusNoConnection; +} + +ConnectionStatus +SBCommunication::AdoptFileDesriptor (int fd, bool owns_fd) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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; + } + + if (log) + log->Printf ("SBCommunication(%p)::AdoptFileDescriptor (fd=%d, ownd_fd=%i) => %s", + m_opaque, fd, owns_fd, Communication::ConnectionStatusAsCString (status)); + + return status; +} + + +ConnectionStatus +SBCommunication::Disconnect () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ConnectionStatus status= eConnectionStatusNoConnection; + if (m_opaque) + status = m_opaque->Disconnect (); + + if (log) + log->Printf ("SBCommunication(%p)::Disconnect () => %s", m_opaque, + Communication::ConnectionStatusAsCString (status)); + + return status; +} + +bool +SBCommunication::IsConnected () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool result = false; + if (m_opaque) + result = m_opaque->IsConnected (); + + if (log) + log->Printf ("SBCommunication(%p)::IsConnected () => %i", m_opaque, result); + + return false; +} + +size_t +SBCommunication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::Read (dst=%p, dst_len=%" PRIu64 ", timeout_usec=%u, &status)...", + m_opaque, + dst, + (uint64_t)dst_len, + timeout_usec); + size_t bytes_read = 0; + if (m_opaque) + bytes_read = m_opaque->Read (dst, dst_len, timeout_usec, status, NULL); + else + status = eConnectionStatusNoConnection; + + if (log) + log->Printf ("SBCommunication(%p)::Read (dst=%p, dst_len=%" PRIu64 ", timeout_usec=%u, &status=%s) => %" PRIu64, + m_opaque, + dst, + (uint64_t)dst_len, + timeout_usec, + Communication::ConnectionStatusAsCString (status), + (uint64_t)bytes_read); + return bytes_read; +} + + +size_t +SBCommunication::Write (const void *src, size_t src_len, ConnectionStatus &status) +{ + size_t bytes_written = 0; + if (m_opaque) + bytes_written = m_opaque->Write (src, src_len, status, NULL); + else + status = eConnectionStatusNoConnection; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::Write (src=%p, src_len=%" PRIu64 ", &status=%s) => %" PRIu64, + m_opaque, src, (uint64_t)src_len, Communication::ConnectionStatusAsCString (status), (uint64_t)bytes_written); + + return 0; +} + +bool +SBCommunication::ReadThreadStart () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool success = false; + if (m_opaque) + success = m_opaque->StartReadThread (); + + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadStart () => %i", m_opaque, success); + + return success; +} + + +bool +SBCommunication::ReadThreadStop () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadStop ()...", m_opaque); + + bool success = false; + if (m_opaque) + success = m_opaque->StopReadThread (); + + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadStop () => %i", m_opaque, success); + + return success; +} + +bool +SBCommunication::ReadThreadIsRunning () +{ + bool result = false; + if (m_opaque) + result = m_opaque->ReadThreadIsRunning (); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBCommunication(%p)::ReadThreadIsRunning () => %i", m_opaque, result); + return result; +} + +bool +SBCommunication::SetReadThreadBytesReceivedCallback +( + ReadThreadBytesReceived callback, + void *callback_baton +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = false; + if (m_opaque) + { + m_opaque->SetReadThreadBytesReceivedCallback (callback, callback_baton); + result = true; + } + + if (log) + log->Printf ("SBCommunication(%p)::SetReadThreadBytesReceivedCallback (callback=%p, baton=%p) => %i", + m_opaque, callback, callback_baton, result); + + return result; +} + +SBBroadcaster +SBCommunication::GetBroadcaster () +{ + SBBroadcaster broadcaster (m_opaque, false); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBCommunication(%p)::GetBroadcaster () => SBBroadcaster (%p)", + m_opaque, broadcaster.get()); + + return broadcaster; +} + +const char * +SBCommunication::GetBroadcasterClass () +{ + return Communication::GetStaticBroadcasterClass().AsCString(); +} + +// +//void +//SBCommunication::CreateIfNeeded () +//{ +// if (m_opaque == NULL) +// { +// static uint32_t g_broadcaster_num; +// char broadcaster_name[256]; +// ::snprintf (name, broadcaster_name, "%p SBCommunication", this); +// m_opaque = new Communication (broadcaster_name); +// m_opaque_owned = true; +// } +// assert (m_opaque); +//} +// +// diff --git a/source/API/SBCompileUnit.cpp b/source/API/SBCompileUnit.cpp new file mode 100644 index 00000000000..9f7487746a8 --- /dev/null +++ b/source/API/SBCompileUnit.cpp @@ -0,0 +1,278 @@ +//===-- SBCompileUnit.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.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 (NULL) +{ +} + +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) +{ +} + +const SBCompileUnit & +SBCompileUnit::operator = (const SBCompileUnit &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + + +SBCompileUnit::~SBCompileUnit () +{ + m_opaque_ptr = NULL; +} + +SBFileSpec +SBCompileUnit::GetFileSpec () const +{ + SBFileSpec file_spec; + if (m_opaque_ptr) + file_spec.SetFileSpec(*m_opaque_ptr); + return file_spec; +} + +uint32_t +SBCompileUnit::GetNumLineEntries () const +{ + 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 +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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); + } + } + + if (log) + { + SBStream sstr; + sb_line_entry.GetDescription (sstr); + log->Printf ("SBCompileUnit(%p)::GetLineEntryAtIndex (idx=%u) => SBLineEntry(%p): '%s'", + m_opaque_ptr, idx, sb_line_entry.get(), sstr.GetData()); + } + + return sb_line_entry; +} + +uint32_t +SBCompileUnit::FindLineEntryIndex (uint32_t start_idx, uint32_t line, SBFileSpec *inline_file_spec) const +{ + 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 +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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() : NULL, + exact, + NULL); + } + + if (log) + { + SBStream sstr; + if (index == UINT32_MAX) + { + log->Printf ("SBCompileUnit(%p)::FindLineEntryIndex (start_idx=%u, line=%u, SBFileSpec(%p)) => NOT FOUND", + m_opaque_ptr, start_idx, line, inline_file_spec ? inline_file_spec->get() : NULL); + } + else + { + log->Printf ("SBCompileUnit(%p)::FindLineEntryIndex (start_idx=%u, line=%u, SBFileSpec(%p)) => %u", + m_opaque_ptr, start_idx, line, inline_file_spec ? inline_file_spec->get() : NULL, index); + } + } + + return index; +} + +uint32_t +SBCompileUnit::GetNumSupportFiles () const +{ + if (m_opaque_ptr) + { + FileSpecList& support_files = m_opaque_ptr->GetSupportFiles (); + return support_files.GetSize(); + } + return 0; +} + + + +lldb::SBTypeList +SBCompileUnit::GetTypes (uint32_t type_mask) +{ + SBTypeList sb_type_list; + + if (m_opaque_ptr) + { + ModuleSP module_sp (m_opaque_ptr->GetModule()); + if (module_sp) + { + SymbolVendor* vendor = module_sp->GetSymbolVendor(); + if (vendor) + { + TypeList type_list; + vendor->GetTypes (m_opaque_ptr, type_mask, type_list); + sb_type_list.m_opaque_ap->Append(type_list); + } + } + } + return sb_type_list; +} + + + + +SBFileSpec +SBCompileUnit::GetSupportFileAtIndex (uint32_t idx) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec sb_file_spec; + if (m_opaque_ptr) + { + FileSpecList &support_files = m_opaque_ptr->GetSupportFiles (); + FileSpec file_spec = support_files.GetFileSpecAtIndex(idx); + sb_file_spec.SetFileSpec(file_spec); + } + + if (log) + { + SBStream sstr; + sb_file_spec.GetDescription (sstr); + log->Printf ("SBCompileUnit(%p)::GetGetFileSpecAtIndex (idx=%u) => SBFileSpec(%p): '%s'", + m_opaque_ptr, idx, sb_file_spec.get(), sstr.GetData()); + } + + return sb_file_spec; +} + +uint32_t +SBCompileUnit::FindSupportFileIndex (uint32_t start_idx, const SBFileSpec &sb_file, bool full) +{ + if (m_opaque_ptr) + { + FileSpecList &support_files = m_opaque_ptr->GetSupportFiles (); + return support_files.FindFileIndex(start_idx, sb_file.ref(), full); + } + return 0; +} + +bool +SBCompileUnit::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +bool +SBCompileUnit::operator == (const SBCompileUnit &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; +} + +bool +SBCompileUnit::operator != (const SBCompileUnit &rhs) const +{ + 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) +{ + Stream &strm = description.ref(); + + if (m_opaque_ptr) + { + m_opaque_ptr->Dump (&strm, false); + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/source/API/SBData.cpp b/source/API/SBData.cpp new file mode 100644 index 00000000000..5b2f075158b --- /dev/null +++ b/source/API/SBData.cpp @@ -0,0 +1,785 @@ +//===-- SBData.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBData.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" + + +using namespace lldb; +using namespace lldb_private; + +SBData::SBData () : + m_opaque_sp(new DataExtractor()) +{ +} + +SBData::SBData (const lldb::DataExtractorSP& data_sp) : + m_opaque_sp (data_sp) +{ +} + +SBData::SBData(const SBData &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBData & +SBData::operator = (const SBData &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *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() +{ + return m_opaque_sp.get() != NULL; +} + +uint8_t +SBData::GetAddressByteSize () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + uint8_t value = 0; + if (m_opaque_sp.get()) + value = m_opaque_sp->GetAddressByteSize(); + if (log) + log->Printf ("SBData::GetAddressByteSize () => " + "(%i)", value); + return value; +} + +void +SBData::SetAddressByteSize (uint8_t addr_byte_size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (m_opaque_sp.get()) + m_opaque_sp->SetAddressByteSize(addr_byte_size); + if (log) + log->Printf ("SBData::SetAddressByteSize (%i)", addr_byte_size); +} + +void +SBData::Clear () +{ + if (m_opaque_sp.get()) + m_opaque_sp->Clear(); +} + +size_t +SBData::GetByteSize () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + size_t value = 0; + if (m_opaque_sp.get()) + value = m_opaque_sp->GetByteSize(); + if (log) + log->Printf ("SBData::GetByteSize () => " + "(%lu)", value); + return value; +} + +lldb::ByteOrder +SBData::GetByteOrder () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::ByteOrder value = eByteOrderInvalid; + if (m_opaque_sp.get()) + value = m_opaque_sp->GetByteOrder(); + if (log) + log->Printf ("SBData::GetByteOrder () => " + "(%i)", value); + return value; +} + +void +SBData::SetByteOrder (lldb::ByteOrder endian) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (m_opaque_sp.get()) + m_opaque_sp->SetByteOrder(endian); + if (log) + log->Printf ("SBData::GetByteOrder (%i)", endian); +} + + +float +SBData::GetFloat (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetFloat (error=%p,offset=%" PRIu64 ") => " + "(%f)", error.get(), offset, value); + return value; +} + +double +SBData::GetDouble (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetDouble (error=%p,offset=%" PRIu64 ") => " + "(%f)", error.get(), offset, value); + return value; +} + +long double +SBData::GetLongDouble (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetLongDouble (error=%p,offset=%" PRIu64 ") => " + "(%Lf)", error.get(), offset, value); + return value; +} + +lldb::addr_t +SBData::GetAddress (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetAddress (error=%p,offset=%" PRIu64 ") => " + "(%p)", error.get(), offset, (void*)value); + return value; +} + +uint8_t +SBData::GetUnsignedInt8 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt8 (error=%p,offset=%" PRIu64 ") => " + "(%c)", error.get(), offset, value); + return value; +} + +uint16_t +SBData::GetUnsignedInt16 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt16 (error=%p,offset=%" PRIu64 ") => " + "(%hd)", error.get(), offset, value); + return value; +} + +uint32_t +SBData::GetUnsignedInt32 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt32 (error=%p,offset=%" PRIu64 ") => " + "(%d)", error.get(), offset, value); + return value; +} + +uint64_t +SBData::GetUnsignedInt64 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetUnsignedInt64 (error=%p,offset=%" PRIu64 ") => " + "(%" PRId64 ")", error.get(), offset, value); + return value; +} + +int8_t +SBData::GetSignedInt8 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetSignedInt8 (error=%p,offset=%" PRIu64 ") => " + "(%c)", error.get(), offset, value); + return value; +} + +int16_t +SBData::GetSignedInt16 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetSignedInt16 (error=%p,offset=%" PRIu64 ") => " + "(%hd)", error.get(), offset, value); + return value; +} + +int32_t +SBData::GetSignedInt32 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetSignedInt32 (error=%p,offset=%" PRIu64 ") => " + "(%d)", error.get(), offset, value); + return value; +} + +int64_t +SBData::GetSignedInt64 (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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"); + } + if (log) + log->Printf ("SBData::GetSignedInt64 (error=%p,offset=%" PRIu64 ") => " + "(%" PRId64 ")", error.get(), offset, value); + return value; +} + +const char* +SBData::GetString (lldb::SBError& error, lldb::offset_t offset) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char* value = 0; + 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 == NULL)) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::GetString (error=%p,offset=%" PRIu64 ") => " + "(%p)", error.get(), offset, value); + return value; +} + +bool +SBData::GetDescription (lldb::SBStream &description, lldb::addr_t base_addr) +{ + Stream &strm = description.ref(); + + if (m_opaque_sp) + { + m_opaque_sp->Dump (&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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + void* ok = NULL; + 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 == NULL)) + error.SetErrorString("unable to read data"); + } + if (log) + log->Printf ("SBData::ReadRawData (error=%p,offset=%" PRIu64 ",buf=%p,size=%lu) => " + "(%p)", error.get(), offset, buf, size, ok); + return ok ? size : 0; +} + +void +SBData::SetData (lldb::SBError& error, + const void *buf, + size_t size, + lldb::ByteOrder endian, + uint8_t addr_size) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (!m_opaque_sp.get()) + m_opaque_sp.reset(new DataExtractor(buf, size, endian, addr_size)); + else + m_opaque_sp->SetData(buf, size, endian); + if (log) + log->Printf ("SBData::SetData (error=%p,buf=%p,size=%lu,endian=%d,addr_size=%c) => " + "(%p)", error.get(), buf, size, endian, addr_size, m_opaque_sp.get()); +} + +bool +SBData::Append (const SBData& rhs) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool value = false; + if (m_opaque_sp.get() && rhs.m_opaque_sp.get()) + value = m_opaque_sp.get()->Append(*rhs.m_opaque_sp); + if (log) + log->Printf ("SBData::Append (rhs=%p) => " + "(%s)", rhs.get(), value ? "true" : "false"); + return value; +} + +lldb::SBData +SBData::CreateDataFromCString (lldb::ByteOrder endian, uint32_t addr_byte_size, const char* data) +{ + if (!data || !data[0]) + return 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 ret; +} + +lldb::SBData +SBData::CreateDataFromUInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint64_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return 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 ret; +} + +lldb::SBData +SBData::CreateDataFromUInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, uint32_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return 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 ret; +} + +lldb::SBData +SBData::CreateDataFromSInt64Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int64_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return 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 ret; +} + +lldb::SBData +SBData::CreateDataFromSInt32Array (lldb::ByteOrder endian, uint32_t addr_byte_size, int32_t* array, size_t array_len) +{ + if (!array || array_len == 0) + return 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 ret; +} + +lldb::SBData +SBData::CreateDataFromDoubleArray (lldb::ByteOrder endian, uint32_t addr_byte_size, double* array, size_t array_len) +{ + if (!array || array_len == 0) + return 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 ret; +} + +bool +SBData::SetDataFromCString (const char* data) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!data) + { + if (log) + log->Printf ("SBData::SetDataFromCString (data=%p) => " + "false", 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.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromCString (data=%p) => " + "true", data); + + return true; +} + +bool +SBData::SetDataFromUInt64Array (uint64_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromUInt64Array (array=%p, array_len = %lu) => " + "false", array, array_len); + 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.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromUInt64Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromUInt32Array (uint32_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromUInt32Array (array=%p, array_len = %lu) => " + "false", array, array_len); + 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.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromUInt32Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromSInt64Array (int64_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromSInt64Array (array=%p, array_len = %lu) => " + "false", array, array_len); + 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.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromSInt64Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromSInt32Array (int32_t* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromSInt32Array (array=%p, array_len = %lu) => " + "false", array, array_len); + 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.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromSInt32Array (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} + +bool +SBData::SetDataFromDoubleArray (double* array, size_t array_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (!array || array_len == 0) + { + if (log) + log->Printf ("SBData::SetDataFromDoubleArray (array=%p, array_len = %lu) => " + "false", array, array_len); + 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.reset(new DataExtractor(buffer_sp, GetByteOrder(), GetAddressByteSize())); + else + m_opaque_sp->SetData(buffer_sp); + + if (log) + log->Printf ("SBData::SetDataFromDoubleArray (array=%p, array_len = %lu) => " + "true", array, array_len); + + return true; +} diff --git a/source/API/SBDebugger.cpp b/source/API/SBDebugger.cpp new file mode 100644 index 00000000000..f5e71d5f1a0 --- /dev/null +++ b/source/API/SBDebugger.cpp @@ -0,0 +1,1264 @@ +//===-- SBDebugger.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" + +#include "lldb/lldb-private.h" + +#include "lldb/API/SBListener.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/SBInputReader.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBTypeCategory.h" +#include "lldb/API/SBTypeFormat.h" +#include "lldb/API/SBTypeFilter.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeSynthetic.h" + + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +using namespace lldb; +using namespace lldb_private; + +void +SBDebugger::Initialize () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger::Initialize ()"); + + SBCommandInterpreter::InitializeSWIG (); + + Debugger::Initialize(); +} + +void +SBDebugger::Terminate () +{ + Debugger::Terminate(); +} + +void +SBDebugger::Clear () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::Clear ()", m_opaque_sp.get()); + + if (m_opaque_sp) + m_opaque_sp->CleanUpInputReaders (); + + m_opaque_sp.reset(); +} + +SBDebugger +SBDebugger::Create() +{ + return SBDebugger::Create(false, NULL, NULL); +} + +SBDebugger +SBDebugger::Create(bool source_init_files) +{ + return SBDebugger::Create (source_init_files, NULL, NULL); +} + +SBDebugger +SBDebugger::Create(bool source_init_files, lldb::LogOutputCallback callback, void *baton) + +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBDebugger debugger; + debugger.reset(Debugger::CreateInstance(callback, baton)); + + if (log) + { + SBStream sstr; + debugger.GetDescription (sstr); + log->Printf ("SBDebugger::Create () => SBDebugger(%p): %s", debugger.m_opaque_sp.get(), sstr.GetData()); + } + + 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) +{ + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + debugger.GetDescription (sstr); + log->Printf ("SBDebugger::Destroy () => SBDebugger(%p): %s", debugger.m_opaque_sp.get(), sstr.GetData()); + } + + Debugger::Destroy (debugger.m_opaque_sp); + + if (debugger.m_opaque_sp.get() != NULL) + debugger.m_opaque_sp.reset(); +} + +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. + Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const bool mandatory = false; + if (log) + { + log->Printf ("SBDebugger::MemoryPressureDetected (), mandatory = %d", mandatory); + } + + ModuleList::RemoveOrphanSharedModules(mandatory); +} + +SBDebugger::SBDebugger () : + m_opaque_sp () +{ +} + +SBDebugger::SBDebugger(const lldb::DebuggerSP &debugger_sp) : + m_opaque_sp(debugger_sp) +{ +} + +SBDebugger::SBDebugger(const SBDebugger &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +SBDebugger & +SBDebugger::operator = (const SBDebugger &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +SBDebugger::~SBDebugger () +{ +} + +bool +SBDebugger::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + + +void +SBDebugger::SetAsync (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->SetAsyncExecution(b); +} + +bool +SBDebugger::GetAsync () +{ + if (m_opaque_sp) + return m_opaque_sp->GetAsyncExecution(); + else + return false; +} + +void +SBDebugger::SkipLLDBInitFiles (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->GetCommandInterpreter().SkipLLDBInitFiles (b); +} + +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) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::SetInputFileHandle (fh=%p, transfer_ownership=%i)", m_opaque_sp.get(), + fh, transfer_ownership); + + if (m_opaque_sp) + m_opaque_sp->SetInputFileHandle (fh, transfer_ownership); +} + +void +SBDebugger::SetOutputFileHandle (FILE *fh, bool transfer_ownership) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + + if (log) + log->Printf ("SBDebugger(%p)::SetOutputFileHandle (fh=%p, transfer_ownership=%i)", m_opaque_sp.get(), + fh, transfer_ownership); + + if (m_opaque_sp) + m_opaque_sp->SetOutputFileHandle (fh, transfer_ownership); +} + +void +SBDebugger::SetErrorFileHandle (FILE *fh, bool transfer_ownership) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + + if (log) + log->Printf ("SBDebugger(%p)::SetErrorFileHandle (fh=%p, transfer_ownership=%i)", m_opaque_sp.get(), + fh, transfer_ownership); + + if (m_opaque_sp) + m_opaque_sp->SetErrorFileHandle (fh, transfer_ownership); +} + +FILE * +SBDebugger::GetInputFileHandle () +{ + if (m_opaque_sp) + return m_opaque_sp->GetInputFile().GetStream(); + return NULL; +} + +FILE * +SBDebugger::GetOutputFileHandle () +{ + if (m_opaque_sp) + return m_opaque_sp->GetOutputFile().GetStream(); + return NULL; +} + +FILE * +SBDebugger::GetErrorFileHandle () +{ + if (m_opaque_sp) + return m_opaque_sp->GetErrorFile().GetStream(); + return NULL; +} + +void +SBDebugger::SaveInputTerminalState() +{ + if (m_opaque_sp) + m_opaque_sp->SaveInputTerminalState(); +} + +void +SBDebugger::RestoreInputTerminalState() +{ + if (m_opaque_sp) + m_opaque_sp->RestoreInputTerminalState(); + +} +SBCommandInterpreter +SBDebugger::GetCommandInterpreter () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBCommandInterpreter sb_interpreter; + if (m_opaque_sp) + sb_interpreter.reset (&m_opaque_sp->GetCommandInterpreter()); + + if (log) + log->Printf ("SBDebugger(%p)::GetCommandInterpreter () => SBCommandInterpreter(%p)", + m_opaque_sp.get(), sb_interpreter.get()); + + return sb_interpreter; +} + +void +SBDebugger::HandleCommand (const char *command) +{ + if (m_opaque_sp) + { + TargetSP target_sp (m_opaque_sp->GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + + SBCommandInterpreter sb_interpreter(GetCommandInterpreter ()); + SBCommandReturnObject result; + + sb_interpreter.HandleCommand (command, result, false); + + if (GetErrorFileHandle() != NULL) + result.PutError (GetErrorFileHandle()); + if (GetOutputFileHandle() != NULL) + result.PutOutput (GetOutputFileHandle()); + + if (m_opaque_sp->GetAsyncExecution() == false) + { + SBProcess process(GetCommandInterpreter().GetProcess ()); + ProcessSP process_sp (process.GetSP()); + if (process_sp) + { + EventSP event_sp; + Listener &lldb_listener = m_opaque_sp->GetListener(); + while (lldb_listener.GetNextEventForBroadcaster (process_sp.get(), event_sp)) + { + SBEvent event(event_sp); + HandleProcessEvent (process, event, GetOutputFileHandle(), GetErrorFileHandle()); + } + } + } + } +} + +SBListener +SBDebugger::GetListener () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBListener sb_listener; + if (m_opaque_sp) + sb_listener.reset(&m_opaque_sp->GetListener(), false); + + if (log) + log->Printf ("SBDebugger(%p)::GetListener () => SBListener(%p)", m_opaque_sp.get(), + sb_listener.get()); + + return sb_listener; +} + +void +SBDebugger::HandleProcessEvent (const SBProcess &process, const SBEvent &event, FILE *out, FILE *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; + + Mutex::Locker api_locker (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 != NULL) + ::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 != NULL) + ::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 () +{ + SBSourceManager sb_source_manager (*this); + return sb_source_manager; +} + + +bool +SBDebugger::GetDefaultArchitecture (char *arch_name, 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) +{ + 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) +{ + + return Args::StringToScriptLanguage (script_language_name, + eScriptLanguageDefault, + NULL); +} + +const char * +SBDebugger::GetVersionString () +{ + return GetVersion(); +} + +const char * +SBDebugger::StateAsCString (StateType state) +{ + return lldb_private::StateAsCString (state); +} + +bool +SBDebugger::StateIsRunningState (StateType state) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const bool result = lldb_private::StateIsRunningState (state); + if (log) + log->Printf ("SBDebugger::StateIsRunningState (state=%s) => %i", + StateAsCString (state), result); + + return result; +} + +bool +SBDebugger::StateIsStoppedState (StateType state) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const bool result = lldb_private::StateIsStoppedState (state, false); + if (log) + log->Printf ("SBDebugger::StateIsStoppedState (state=%s) => %i", + StateAsCString (state), result); + + 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) +{ + 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, + &platform_options, + target_sp); + + if (sb_error.Success()) + sb_target.SetSP (target_sp); + } + else + { + sb_error.SetErrorString("invalid target"); + } + + 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)", + m_opaque_sp.get(), + filename, + target_triple, + platform_name, + add_dependent_modules, + sb_error.GetCString(), + target_sp.get()); + } + + return sb_target; +} + +SBTarget +SBDebugger::CreateTargetWithFileAndTargetTriple (const char *filename, + const char *target_triple) +{ + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + const bool add_dependent_modules = true; + Error error (m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + target_triple, + add_dependent_modules, + NULL, + 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)", + m_opaque_sp.get(), filename, target_triple, target_sp.get()); + } + + return sb_target; +} + +SBTarget +SBDebugger::CreateTargetWithFileAndArch (const char *filename, const char *arch_cstr) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + Error error; + const bool add_dependent_modules = true; + + error = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + arch_cstr, + add_dependent_modules, + NULL, + 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)", + m_opaque_sp.get(), filename, arch_cstr, target_sp.get()); + } + + return sb_target; +} + +SBTarget +SBDebugger::CreateTarget (const char *filename) +{ + SBTarget sb_target; + TargetSP target_sp; + if (m_opaque_sp) + { + ArchSpec arch = Target::GetDefaultArchitecture (); + Error error; + const bool add_dependent_modules = true; + + PlatformSP platform_sp(m_opaque_sp->GetPlatformList().GetSelectedPlatform()); + error = m_opaque_sp->GetTargetList().CreateTarget (*m_opaque_sp, + filename, + arch, + add_dependent_modules, + platform_sp, + 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)", + m_opaque_sp.get(), filename, target_sp.get()); + } + return sb_target; +} + +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", m_opaque_sp.get(), target.m_opaque_sp.get(), result); + } + + return result; +} +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 sb_target; +} + +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 (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 sb_target; +} + +SBTarget +SBDebugger::FindTargetWithFileAndArch (const char *filename, const char *arch_name) +{ + SBTarget sb_target; + if (m_opaque_sp && filename && filename[0]) + { + // No need to lock, the target list is thread safe + ArchSpec arch (arch_name, m_opaque_sp->GetPlatformList().GetSelectedPlatform().get()); + TargetSP target_sp (m_opaque_sp->GetTargetList().FindTargetWithExecutableAndArchitecture (FileSpec(filename, false), arch_name ? &arch : NULL)); + sb_target.SetSP (target_sp); + } + return 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 () +{ + 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 () +{ + 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", m_opaque_sp.get(), + target_sp.get(), sstr.GetData()); + } + + return sb_target; +} + +void +SBDebugger::SetSelectedTarget (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", m_opaque_sp.get(), + target_sp.get(), sstr.GetData()); + } +} + +void +SBDebugger::DispatchInput (void* baton, const void *data, size_t data_len) +{ + DispatchInput (data,data_len); +} + +void +SBDebugger::DispatchInput (const void *data, size_t 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 () +{ + if (m_opaque_sp) + m_opaque_sp->DispatchInputInterrupt (); +} + +void +SBDebugger::DispatchInputEndOfFile () +{ + if (m_opaque_sp) + m_opaque_sp->DispatchInputEndOfFile (); +} + +bool +SBDebugger::InputReaderIsTopReader (const lldb::SBInputReader &reader) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::InputReaderIsTopReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); + + if (m_opaque_sp && reader.IsValid()) + { + InputReaderSP reader_sp (*reader); + return m_opaque_sp->InputReaderIsTopReader (reader_sp); + } + + return false; +} + + +void +SBDebugger::PushInputReader (SBInputReader &reader) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::PushInputReader (SBInputReader(%p))", m_opaque_sp.get(), &reader); + + if (m_opaque_sp && reader.IsValid()) + { + TargetSP target_sp (m_opaque_sp->GetSelectedTarget()); + Mutex::Locker api_locker; + if (target_sp) + api_locker.Lock(target_sp->GetAPIMutex()); + InputReaderSP reader_sp(*reader); + m_opaque_sp->PushInputReader (reader_sp); + } +} + +void +SBDebugger::NotifyTopInputReader (InputReaderAction notification) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::NotifyTopInputReader (%d)", m_opaque_sp.get(), notification); + + if (m_opaque_sp) + m_opaque_sp->NotifyTopInputReader (notification); +} + +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) +{ + // 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 sb_debugger; +} + +const char * +SBDebugger::GetInstanceName() +{ + if (m_opaque_sp) + return m_opaque_sp->GetInstanceName().AsCString(); + else + return NULL; +} + +SBError +SBDebugger::SetInternalVariable (const char *var_name, const char *value, const char *debugger_instance_name) +{ + SBError sb_error; + DebuggerSP debugger_sp(Debugger::FindDebuggerWithInstanceName (ConstString(debugger_instance_name))); + Error 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 sb_error; +} + +SBStringList +SBDebugger::GetInternalVariableValue (const char *var_name, const char *debugger_instance_name) +{ + SBStringList ret_value; + DebuggerSP debugger_sp(Debugger::FindDebuggerWithInstanceName (ConstString(debugger_instance_name))); + Error 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.c_str(), value_str.size()); + return SBStringList(&string_list); + } + } + } + return SBStringList(); +} + +uint32_t +SBDebugger::GetTerminalWidth () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetTerminalWidth (); + return 0; +} + +void +SBDebugger::SetTerminalWidth (uint32_t term_width) +{ + if (m_opaque_sp) + m_opaque_sp->SetTerminalWidth (term_width); +} + +const char * +SBDebugger::GetPrompt() const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBDebugger(%p)::GetPrompt () => \"%s\"", m_opaque_sp.get(), + (m_opaque_sp ? m_opaque_sp->GetPrompt() : "")); + + if (m_opaque_sp) + return m_opaque_sp->GetPrompt (); + return 0; +} + +void +SBDebugger::SetPrompt (const char *prompt) +{ + if (m_opaque_sp) + m_opaque_sp->SetPrompt (prompt); +} + + +ScriptLanguage +SBDebugger::GetScriptLanguage() const +{ + if (m_opaque_sp) + return m_opaque_sp->GetScriptLanguage (); + return eScriptLanguageNone; +} + +void +SBDebugger::SetScriptLanguage (ScriptLanguage script_lang) +{ + if (m_opaque_sp) + { + m_opaque_sp->SetScriptLanguage (script_lang); + } +} + +bool +SBDebugger::SetUseExternalEditor (bool value) +{ + if (m_opaque_sp) + return m_opaque_sp->SetUseExternalEditor (value); + return false; +} + +bool +SBDebugger::GetUseExternalEditor () +{ + if (m_opaque_sp) + return m_opaque_sp->GetUseExternalEditor (); + return false; +} + +bool +SBDebugger::SetUseColor (bool value) +{ + if (m_opaque_sp) + return m_opaque_sp->SetUseColor (value); + return false; +} + +bool +SBDebugger::GetUseColor () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetUseColor (); + return false; +} + +bool +SBDebugger::GetDescription (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() +{ + if (m_opaque_sp) + return m_opaque_sp->GetID(); + return LLDB_INVALID_UID; +} + + +SBError +SBDebugger::SetCurrentPlatform (const char *platform_name) +{ + SBError sb_error; + if (m_opaque_sp) + { + PlatformSP platform_sp (Platform::Create (platform_name, sb_error.ref())); + + if (platform_sp) + { + bool make_selected = true; + m_opaque_sp->GetPlatformList().Append (platform_sp, make_selected); + } + } + return sb_error; +} + +bool +SBDebugger::SetCurrentPlatformSDKRoot (const char *sysroot) +{ + if (m_opaque_sp) + { + PlatformSP platform_sp (m_opaque_sp->GetPlatformList().GetSelectedPlatform()); + + if (platform_sp) + { + platform_sp->SetSDKRootDirectory (ConstString (sysroot)); + return true; + } + } + return false; +} + +bool +SBDebugger::GetCloseInputOnEOF () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetCloseInputOnEOF (); + return false; +} + +void +SBDebugger::SetCloseInputOnEOF (bool b) +{ + if (m_opaque_sp) + m_opaque_sp->SetCloseInputOnEOF (b); +} + +SBTypeCategory +SBDebugger::GetCategory (const char* category_name) +{ + if (!category_name || *category_name == 0) + return SBTypeCategory(); + + TypeCategoryImplSP category_sp; + + if (DataVisualization::Categories::GetCategory(ConstString(category_name), category_sp, false)) + return SBTypeCategory(category_sp); + else + return SBTypeCategory(); +} + +SBTypeCategory +SBDebugger::CreateCategory (const char* category_name) +{ + if (!category_name || *category_name == 0) + return SBTypeCategory(); + + TypeCategoryImplSP category_sp; + + if (DataVisualization::Categories::GetCategory(ConstString(category_name), category_sp, true)) + return SBTypeCategory(category_sp); + else + return SBTypeCategory(); +} + +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() +{ + return DataVisualization::Categories::GetCount(); +} + +SBTypeCategory +SBDebugger::GetCategoryAtIndex (uint32_t index) +{ + return SBTypeCategory(DataVisualization::Categories::GetCategoryAtIndex(index)); +} + +SBTypeCategory +SBDebugger::GetDefaultCategory() +{ + return GetCategory("default"); +} + +SBTypeFormat +SBDebugger::GetFormatForType (SBTypeNameSpecifier type_name) +{ + SBTypeCategory default_category_sb = GetDefaultCategory(); + if (default_category_sb.GetEnabled()) + return default_category_sb.GetFormatForType(type_name); + return SBTypeFormat(); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSummary +SBDebugger::GetSummaryForType (SBTypeNameSpecifier type_name) +{ + if (type_name.IsValid() == false) + return SBTypeSummary(); + return SBTypeSummary(DataVisualization::GetSummaryForType(type_name.GetSP())); +} +#endif // LLDB_DISABLE_PYTHON + +SBTypeFilter +SBDebugger::GetFilterForType (SBTypeNameSpecifier type_name) +{ + if (type_name.IsValid() == false) + return SBTypeFilter(); + return SBTypeFilter(DataVisualization::GetFilterForType(type_name.GetSP())); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSynthetic +SBDebugger::GetSyntheticForType (SBTypeNameSpecifier type_name) +{ + if (type_name.IsValid() == false) + return SBTypeSynthetic(); + return SBTypeSynthetic(DataVisualization::GetSyntheticForType(type_name.GetSP())); +} +#endif // LLDB_DISABLE_PYTHON + +bool +SBDebugger::EnableLog (const char *channel, const char **categories) +{ + if (m_opaque_sp) + { + uint32_t log_options = LLDB_LOG_OPTION_PREPEND_TIMESTAMP | LLDB_LOG_OPTION_PREPEND_THREAD_NAME; + StreamString errors; + return m_opaque_sp->EnableLog (channel, categories, NULL, log_options, errors); + + } + else + return false; +} + +void +SBDebugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) +{ + if (m_opaque_sp) + { + return m_opaque_sp->SetLoggingCallback (log_callback, baton); + } +} + + diff --git a/source/API/SBDeclaration.cpp b/source/API/SBDeclaration.cpp new file mode 100644 index 00000000000..fc90156e75a --- /dev/null +++ b/source/API/SBDeclaration.cpp @@ -0,0 +1,206 @@ +//===-- SBDeclaration.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBDeclaration.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/Declaration.h" + +#include + +using namespace lldb; +using namespace lldb_private; + + +SBDeclaration::SBDeclaration () : + m_opaque_ap () +{ +} + +SBDeclaration::SBDeclaration (const SBDeclaration &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + ref() = rhs.ref(); +} + +SBDeclaration::SBDeclaration (const lldb_private::Declaration *lldb_object_ptr) : + m_opaque_ap () +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; +} + +const SBDeclaration & +SBDeclaration::operator = (const SBDeclaration &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + ref() = rhs.ref(); + else + m_opaque_ap.reset(); + } + return *this; +} + +void +SBDeclaration::SetDeclaration (const lldb_private::Declaration &lldb_object_ref) +{ + ref() = lldb_object_ref; +} + + +SBDeclaration::~SBDeclaration () +{ +} + + +bool +SBDeclaration::IsValid () const +{ + return m_opaque_ap.get() && m_opaque_ap->IsValid(); +} + + +SBFileSpec +SBDeclaration::GetFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec sb_file_spec; + if (m_opaque_ap.get() && m_opaque_ap->GetFile()) + sb_file_spec.SetFileSpec(m_opaque_ap->GetFile()); + + if (log) + { + SBStream sstr; + sb_file_spec.GetDescription (sstr); + log->Printf ("SBLineEntry(%p)::GetFileSpec () => SBFileSpec(%p): %s", m_opaque_ap.get(), + sb_file_spec.get(), sstr.GetData()); + } + + return sb_file_spec; +} + +uint32_t +SBDeclaration::GetLine () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t line = 0; + if (m_opaque_ap.get()) + line = m_opaque_ap->GetLine(); + + if (log) + log->Printf ("SBLineEntry(%p)::GetLine () => %u", m_opaque_ap.get(), line); + + return line; +} + + +uint32_t +SBDeclaration::GetColumn () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetColumn(); + return 0; +} + +void +SBDeclaration::SetFileSpec (lldb::SBFileSpec filespec) +{ + if (filespec.IsValid()) + ref().SetFile(filespec.ref()); + else + ref().SetFile(FileSpec()); +} +void +SBDeclaration::SetLine (uint32_t line) +{ + ref().SetLine(line); +} + +void +SBDeclaration::SetColumn (uint32_t column) +{ + ref().SetColumn(column); +} + + + +bool +SBDeclaration::operator == (const SBDeclaration &rhs) const +{ + lldb_private::Declaration *lhs_ptr = m_opaque_ap.get(); + lldb_private::Declaration *rhs_ptr = rhs.m_opaque_ap.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_private::Declaration *lhs_ptr = m_opaque_ap.get(); + lldb_private::Declaration *rhs_ptr = rhs.m_opaque_ap.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_ap.get(); +} + +lldb_private::Declaration & +SBDeclaration::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new lldb_private::Declaration ()); + return *m_opaque_ap; +} + +const lldb_private::Declaration & +SBDeclaration::ref() const +{ + return *m_opaque_ap; +} + +bool +SBDeclaration::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + char file_path[PATH_MAX*2]; + m_opaque_ap->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_ap.get(); +} diff --git a/source/API/SBError.cpp b/source/API/SBError.cpp new file mode 100644 index 00000000000..bd6b54300f6 --- /dev/null +++ b/source/API/SBError.cpp @@ -0,0 +1,233 @@ +//===-- SBError.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBError.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" + +#include + +using namespace lldb; +using namespace lldb_private; + + +SBError::SBError () : + m_opaque_ap () +{ +} + +SBError::SBError (const SBError &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + m_opaque_ap.reset (new Error(*rhs)); +} + + +SBError::~SBError() +{ +} + +const SBError & +SBError::operator = (const SBError &rhs) +{ + if (rhs.IsValid()) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *rhs; + else + m_opaque_ap.reset (new Error(*rhs)); + } + else + m_opaque_ap.reset(); + + return *this; +} + + +const char * +SBError::GetCString () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->AsCString(); + return NULL; +} + +void +SBError::Clear () +{ + if (m_opaque_ap.get()) + m_opaque_ap->Clear(); +} + +bool +SBError::Fail () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_value = false; + if (m_opaque_ap.get()) + ret_value = m_opaque_ap->Fail(); + + if (log) + log->Printf ("SBError(%p)::Fail () => %i", m_opaque_ap.get(), ret_value); + + return ret_value; +} + +bool +SBError::Success () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool ret_value = true; + if (m_opaque_ap.get()) + ret_value = m_opaque_ap->Success(); + + if (log) + log->Printf ("SBError(%p)::Success () => %i", m_opaque_ap.get(), ret_value); + + return ret_value; +} + +uint32_t +SBError::GetError () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t err = 0; + if (m_opaque_ap.get()) + err = m_opaque_ap->GetError(); + + if (log) + log->Printf ("SBError(%p)::GetError () => 0x%8.8x", m_opaque_ap.get(), err); + + + return err; +} + +ErrorType +SBError::GetType () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ErrorType err_type = eErrorTypeInvalid; + if (m_opaque_ap.get()) + err_type = m_opaque_ap->GetType(); + + if (log) + log->Printf ("SBError(%p)::GetType () => %i", m_opaque_ap.get(), err_type); + + return err_type; +} + +void +SBError::SetError (uint32_t err, ErrorType type) +{ + CreateIfNeeded (); + m_opaque_ap->SetError (err, type); +} + +void +SBError::SetError (const Error &lldb_error) +{ + CreateIfNeeded (); + *m_opaque_ap = lldb_error; +} + + +void +SBError::SetErrorToErrno () +{ + CreateIfNeeded (); + m_opaque_ap->SetErrorToErrno (); +} + +void +SBError::SetErrorToGenericError () +{ + CreateIfNeeded (); + m_opaque_ap->SetErrorToErrno (); +} + +void +SBError::SetErrorString (const char *err_str) +{ + CreateIfNeeded (); + m_opaque_ap->SetErrorString (err_str); +} + +int +SBError::SetErrorStringWithFormat (const char *format, ...) +{ + CreateIfNeeded (); + va_list args; + va_start (args, format); + int num_chars = m_opaque_ap->SetErrorStringWithVarArg (format, args); + va_end (args); + return num_chars; +} + +bool +SBError::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + +void +SBError::CreateIfNeeded () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset(new Error ()); +} + + +lldb_private::Error * +SBError::operator->() +{ + return m_opaque_ap.get(); +} + +lldb_private::Error * +SBError::get() +{ + return m_opaque_ap.get(); +} + +lldb_private::Error & +SBError::ref() +{ + CreateIfNeeded(); + return *m_opaque_ap; +} + +const lldb_private::Error & +SBError::operator*() const +{ + // Be sure to call "IsValid()" before calling this function or it will crash + return *m_opaque_ap; +} + +bool +SBError::GetDescription (SBStream &description) +{ + if (m_opaque_ap.get()) + { + if (m_opaque_ap->Success()) + description.Printf ("success"); + else + { + const char * err_string = GetCString(); + description.Printf ("error: %s", (err_string != NULL ? err_string : "")); + } + } + else + description.Printf ("error: "); + + return true; +} diff --git a/source/API/SBEvent.cpp b/source/API/SBEvent.cpp new file mode 100644 index 00000000000..d5d4a84bc1f --- /dev/null +++ b/source/API/SBEvent.cpp @@ -0,0 +1,245 @@ +//===-- SBEvent.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Event.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Target/Process.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + + +SBEvent::SBEvent () : + m_event_sp (), + m_opaque_ptr (NULL) +{ +} + +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()) +{ +} + +SBEvent::SBEvent (EventSP &event_sp) : + m_event_sp (event_sp), + m_opaque_ptr (event_sp.get()) +{ +} + +SBEvent::SBEvent (const SBEvent &rhs) : + m_event_sp (rhs.m_event_sp), + m_opaque_ptr (rhs.m_opaque_ptr) +{ + +} + +const SBEvent & +SBEvent::operator = (const SBEvent &rhs) +{ + if (this != &rhs) + { + m_event_sp = rhs.m_event_sp; + m_opaque_ptr = rhs.m_opaque_ptr; + } + return *this; +} + +SBEvent::~SBEvent() +{ +} + +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 NULL; +} + +uint32_t +SBEvent::GetType () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const Event *lldb_event = get(); + uint32_t event_type = 0; + if (lldb_event) + event_type = lldb_event->GetType(); + + if (log) + { + StreamString sstr; + if (lldb_event && lldb_event->GetBroadcaster() && lldb_event->GetBroadcaster()->GetEventNames(sstr, event_type, true)) + log->Printf ("SBEvent(%p)::GetType () => 0x%8.8x (%s)", get(), event_type, sstr.GetData()); + else + log->Printf ("SBEvent(%p)::GetType () => 0x%8.8x", get(), event_type); + + } + + return event_type; +} + +SBBroadcaster +SBEvent::GetBroadcaster () const +{ + SBBroadcaster broadcaster; + const Event *lldb_event = get(); + if (lldb_event) + broadcaster.reset (lldb_event->GetBroadcaster(), false); + return broadcaster; +} + +const char * +SBEvent::GetBroadcasterClass () const +{ + const Event *lldb_event = get(); + if (lldb_event) + return lldb_event->GetBroadcaster()->GetBroadcasterClass().AsCString(); + else + return "unknown class"; +} + +bool +SBEvent::BroadcasterMatchesPtr (const SBBroadcaster *broadcaster) +{ + if (broadcaster) + return BroadcasterMatchesRef (*broadcaster); + return false; +} + +bool +SBEvent::BroadcasterMatchesRef (const SBBroadcaster &broadcaster) +{ + + Event *lldb_event = get(); + bool success = false; + if (lldb_event) + success = lldb_event->BroadcasterIs (broadcaster.get()); + + // For logging, this gets a little chatty so only enable this when verbose logging is on + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API | LIBLLDB_LOG_VERBOSE)); + if (log) + log->Printf ("SBEvent(%p)::BroadcasterMatchesRef (SBBroadcaster(%p): %s) => %i", + get(), + broadcaster.get(), + broadcaster.GetName(), + success); + + return success; +} + +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 +{ + // Do NOT use m_opaque_ptr directly!!! Must use the SBEvent::get() + // accessor. See comments in SBEvent::get().... + return SBEvent::get() != NULL; + +} + +const char * +SBEvent::GetCStringFromEvent (const SBEvent &event) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBEvent(%p)::GetCStringFromEvent () => \"%s\"", + event.get(), + reinterpret_cast(EventDataBytes::GetBytesFromEvent (event.get()))); + + return reinterpret_cast(EventDataBytes::GetBytesFromEvent (event.get())); +} + + +bool +SBEvent::GetDescription (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 +{ + Stream &strm = description.ref(); + + if (get()) + { + m_opaque_ptr->Dump (&strm); + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/source/API/SBExpressionOptions.cpp b/source/API/SBExpressionOptions.cpp new file mode 100644 index 00000000000..127b0cf13cd --- /dev/null +++ b/source/API/SBExpressionOptions.cpp @@ -0,0 +1,126 @@ +//===-- SBExpressionOptions.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBStream.h" + +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBExpressionOptions::SBExpressionOptions () : + m_opaque_ap(new EvaluateExpressionOptions()) +{ +} + +SBExpressionOptions::SBExpressionOptions (const SBExpressionOptions &rhs) +{ + m_opaque_ap.reset(new EvaluateExpressionOptions()); + *(m_opaque_ap.get()) = rhs.ref(); +} + +const SBExpressionOptions & +SBExpressionOptions::operator = (const SBExpressionOptions &rhs) +{ + if (this != &rhs) + { + this->ref() = rhs.ref(); + } + return *this; +} + +SBExpressionOptions::~SBExpressionOptions() +{ +} + +bool +SBExpressionOptions::GetCoerceResultToId () const +{ + return m_opaque_ap->DoesCoerceToId (); +} + +void +SBExpressionOptions::SetCoerceResultToId (bool coerce) +{ + m_opaque_ap->SetCoerceToId (coerce); +} + +bool +SBExpressionOptions::GetUnwindOnError () const +{ + return m_opaque_ap->DoesUnwindOnError (); +} + +void +SBExpressionOptions::SetUnwindOnError (bool unwind) +{ + m_opaque_ap->SetUnwindOnError (unwind); +} + +bool +SBExpressionOptions::GetIgnoreBreakpoints () const +{ + return m_opaque_ap->DoesIgnoreBreakpoints (); +} + +void +SBExpressionOptions::SetIgnoreBreakpoints (bool ignore) +{ + m_opaque_ap->SetIgnoreBreakpoints (ignore); +} + +lldb::DynamicValueType +SBExpressionOptions::GetFetchDynamicValue () const +{ + return m_opaque_ap->GetUseDynamic (); +} + +void +SBExpressionOptions::SetFetchDynamicValue (lldb::DynamicValueType dynamic) +{ + m_opaque_ap->SetUseDynamic (dynamic); +} + +uint32_t +SBExpressionOptions::GetTimeoutInMicroSeconds () const +{ + return m_opaque_ap->GetTimeoutUsec (); +} + +void +SBExpressionOptions::SetTimeoutInMicroSeconds (uint32_t timeout) +{ + m_opaque_ap->SetTimeoutUsec (timeout); +} + +bool +SBExpressionOptions::GetTryAllThreads () const +{ + return m_opaque_ap->GetRunOthers (); +} + +void +SBExpressionOptions::SetTryAllThreads (bool run_others) +{ + m_opaque_ap->SetRunOthers (run_others); +} + +EvaluateExpressionOptions * +SBExpressionOptions::get() const +{ + return m_opaque_ap.get(); +} + +EvaluateExpressionOptions & +SBExpressionOptions::ref () const +{ + return *(m_opaque_ap.get()); +} diff --git a/source/API/SBFileSpec.cpp b/source/API/SBFileSpec.cpp new file mode 100644 index 00000000000..4413689501a --- /dev/null +++ b/source/API/SBFileSpec.cpp @@ -0,0 +1,181 @@ +//===-- SBFileSpec.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBStream.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpec::SBFileSpec () : + m_opaque_ap(new lldb_private::FileSpec()) +{ +} + +SBFileSpec::SBFileSpec (const SBFileSpec &rhs) : + m_opaque_ap(new lldb_private::FileSpec(*rhs.m_opaque_ap)) +{ +} + +SBFileSpec::SBFileSpec (const lldb_private::FileSpec& fspec) : + m_opaque_ap(new lldb_private::FileSpec(fspec)) +{ +} + +// Deprected!!! +SBFileSpec::SBFileSpec (const char *path) : + m_opaque_ap(new FileSpec (path, true)) +{ +} + +SBFileSpec::SBFileSpec (const char *path, bool resolve) : + m_opaque_ap(new FileSpec (path, resolve)) +{ +} + +SBFileSpec::~SBFileSpec () +{ +} + +const SBFileSpec & +SBFileSpec::operator = (const SBFileSpec &rhs) +{ + if (this != &rhs) + *m_opaque_ap = *rhs.m_opaque_ap; + return *this; +} + +bool +SBFileSpec::IsValid() const +{ + return *m_opaque_ap; +} + +bool +SBFileSpec::Exists () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = m_opaque_ap->Exists(); + + if (log) + log->Printf ("SBFileSpec(%p)::Exists () => %s", m_opaque_ap.get(), (result ? "true" : "false")); + + return result; +} + +bool +SBFileSpec::ResolveExecutableLocation () +{ + return m_opaque_ap->ResolveExecutableLocation (); +} + +int +SBFileSpec::ResolvePath (const char *src_path, char *dst_path, size_t dst_len) +{ + return lldb_private::FileSpec::Resolve (src_path, dst_path, dst_len); +} + +const char * +SBFileSpec::GetFilename() const +{ + const char *s = m_opaque_ap->GetFilename().AsCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (s) + log->Printf ("SBFileSpec(%p)::GetFilename () => \"%s\"", m_opaque_ap.get(), s); + else + log->Printf ("SBFileSpec(%p)::GetFilename () => NULL", m_opaque_ap.get()); + } + + return s; +} + +const char * +SBFileSpec::GetDirectory() const +{ + const char *s = m_opaque_ap->GetDirectory().AsCString(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (s) + log->Printf ("SBFileSpec(%p)::GetDirectory () => \"%s\"", m_opaque_ap.get(), s); + else + log->Printf ("SBFileSpec(%p)::GetDirectory () => NULL", m_opaque_ap.get()); + } + return s; +} + +uint32_t +SBFileSpec::GetPath (char *dst_path, size_t dst_len) const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t result = m_opaque_ap->GetPath (dst_path, dst_len); + + if (log) + log->Printf ("SBFileSpec(%p)::GetPath (dst_path=\"%.*s\", dst_len=%" PRIu64 ") => %u", + m_opaque_ap.get(), result, dst_path, (uint64_t)dst_len, result); + + if (result == 0 && dst_path && dst_len > 0) + *dst_path = '\0'; + return result; +} + + +const lldb_private::FileSpec * +SBFileSpec::operator->() const +{ + return m_opaque_ap.get(); +} + +const lldb_private::FileSpec * +SBFileSpec::get() const +{ + return m_opaque_ap.get(); +} + + +const lldb_private::FileSpec & +SBFileSpec::operator*() const +{ + return *m_opaque_ap.get(); +} + +const lldb_private::FileSpec & +SBFileSpec::ref() const +{ + return *m_opaque_ap.get(); +} + + +void +SBFileSpec::SetFileSpec (const lldb_private::FileSpec& fs) +{ + *m_opaque_ap = fs; +} + +bool +SBFileSpec::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + char path[PATH_MAX]; + if (m_opaque_ap->GetPath(path, sizeof(path))) + strm.PutCString (path); + return true; +} diff --git a/source/API/SBFileSpecList.cpp b/source/API/SBFileSpecList.cpp new file mode 100644 index 00000000000..3ebf3cc80a2 --- /dev/null +++ b/source/API/SBFileSpecList.cpp @@ -0,0 +1,142 @@ +//===-- SBFileSpecListList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBFileSpecList.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/FileSpec.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpecList::SBFileSpecList () : + m_opaque_ap(new FileSpecList()) +{ +} + +SBFileSpecList::SBFileSpecList (const SBFileSpecList &rhs) : + m_opaque_ap() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (rhs.m_opaque_ap.get()) + m_opaque_ap.reset (new FileSpecList (*(rhs.get()))); + + if (log) + { + log->Printf ("SBFileSpecList::SBFileSpecList (const SBFileSpecList rhs.ap=%p) => SBFileSpecList(%p)", + rhs.m_opaque_ap.get(), m_opaque_ap.get()); + } +} + +SBFileSpecList::~SBFileSpecList () +{ +} + +const SBFileSpecList & +SBFileSpecList::operator = (const SBFileSpecList &rhs) +{ + if (this != &rhs) + { + m_opaque_ap.reset (new lldb_private::FileSpecList(*(rhs.get()))); + } + return *this; +} + +uint32_t +SBFileSpecList::GetSize () const +{ + return m_opaque_ap->GetSize(); +} + +void +SBFileSpecList::Append (const SBFileSpec &sb_file) +{ + m_opaque_ap->Append (sb_file.ref()); +} + +bool +SBFileSpecList::AppendIfUnique (const SBFileSpec &sb_file) +{ + return m_opaque_ap->AppendIfUnique (sb_file.ref()); +} + +void +SBFileSpecList::Clear() +{ + m_opaque_ap->Clear(); +} + +uint32_t +SBFileSpecList::FindFileIndex (uint32_t idx, const SBFileSpec &sb_file, bool full) +{ + return m_opaque_ap->FindFileIndex (idx, sb_file.ref(), full); +} + +const SBFileSpec +SBFileSpecList::GetFileSpecAtIndex (uint32_t idx) const +{ + SBFileSpec new_spec; + new_spec.SetFileSpec(m_opaque_ap->GetFileSpecAtIndex(idx)); + return new_spec; +} + +const lldb_private::FileSpecList * +SBFileSpecList::operator->() const +{ + return m_opaque_ap.get(); +} + +const lldb_private::FileSpecList * +SBFileSpecList::get() const +{ + return m_opaque_ap.get(); +} + + +const lldb_private::FileSpecList & +SBFileSpecList::operator*() const +{ + return *m_opaque_ap.get(); +} + +const lldb_private::FileSpecList & +SBFileSpecList::ref() const +{ + return *m_opaque_ap.get(); +} + +bool +SBFileSpecList::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + uint32_t num_files = m_opaque_ap->GetSize(); + strm.Printf ("%d files: ", num_files); + for (uint32_t i = 0; i < num_files; i++) + { + char path[PATH_MAX]; + if (m_opaque_ap->GetFileSpecAtIndex(i).GetPath(path, sizeof(path))) + strm.Printf ("\n %s", path); + } + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/source/API/SBFrame.cpp b/source/API/SBFrame.cpp new file mode 100644 index 00000000000..1a1a63bd067 --- /dev/null +++ b/source/API/SBFrame.cpp @@ -0,0 +1,1526 @@ +//===-- SBFrame.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBFrame.h" + +#include +#include + +#include "lldb/lldb-types.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangUserExpression.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/VariableList.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.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 "lldb/API/SBDebugger.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBExpressionOptions.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBThread.h" + +using namespace lldb; +using namespace lldb_private; + + +SBFrame::SBFrame () : + m_opaque_sp (new ExecutionContextRef()) +{ +} + +SBFrame::SBFrame (const StackFrameSP &lldb_object_sp) : + m_opaque_sp (new ExecutionContextRef (lldb_object_sp)) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + GetDescription (sstr); + log->Printf ("SBFrame::SBFrame (sp=%p) => SBFrame(%p): %s", + lldb_object_sp.get(), lldb_object_sp.get(), sstr.GetData()); + + } +} + +SBFrame::SBFrame(const SBFrame &rhs) : + m_opaque_sp (new ExecutionContextRef (*rhs.m_opaque_sp)) +{ +} + +const SBFrame & +SBFrame::operator = (const SBFrame &rhs) +{ + if (this != &rhs) + *m_opaque_sp = *rhs.m_opaque_sp; + return *this; +} + +SBFrame::~SBFrame() +{ +} + +StackFrameSP +SBFrame::GetFrameSP() const +{ + if (m_opaque_sp) + return m_opaque_sp->GetFrameSP(); + return StackFrameSP(); +} + +void +SBFrame::SetFrameSP (const StackFrameSP &lldb_object_sp) +{ + return m_opaque_sp->SetFrameSP(lldb_object_sp); +} + +bool +SBFrame::IsValid() const +{ + return GetFrameSP().get() != NULL; +} + +SBSymbolContext +SBFrame::GetSymbolContext (uint32_t resolve_scope) const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBSymbolContext sb_sym_ctx; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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 (resolve_scope)); + } + else + { + if (log) + log->Printf ("SBFrame::GetVariables () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetSymbolContext () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetSymbolContext (resolve_scope=0x%8.8x) => SBSymbolContext(%p)", + frame, resolve_scope, sb_sym_ctx.get()); + + return sb_sym_ctx; +} + +SBModule +SBFrame::GetModule () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBModule sb_module; + ModuleSP module_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetModule () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetModule () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetModule () => SBModule(%p)", + frame, module_sp.get()); + + return sb_module; +} + +SBCompileUnit +SBFrame::GetCompileUnit () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBCompileUnit sb_comp_unit; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetCompileUnit () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetCompileUnit () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetCompileUnit () => SBCompileUnit(%p)", + frame, sb_comp_unit.get()); + + return sb_comp_unit; +} + +SBFunction +SBFrame::GetFunction () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBFunction sb_function; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetFunction () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFunction () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetFunction () => SBFunction(%p)", + frame, sb_function.get()); + + return sb_function; +} + +SBSymbol +SBFrame::GetSymbol () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBSymbol sb_symbol; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetSymbol () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetSymbol () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetSymbol () => SBSymbol(%p)", + frame, sb_symbol.get()); + return sb_symbol; +} + +SBBlock +SBFrame::GetBlock () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBBlock sb_block; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetBlock () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame(%p)::GetBlock () => error: process is running", frame); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetBlock () => SBBlock(%p)", + frame, sb_block.GetPtr()); + return sb_block; +} + +SBBlock +SBFrame::GetFrameBlock () const +{ + SBBlock sb_block; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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 ()); + } + else + { + if (log) + log->Printf ("SBFrame::GetFrameBlock () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFrameBlock () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetFrameBlock () => SBBlock(%p)", + frame, sb_block.GetPtr()); + return sb_block; +} + +SBLineEntry +SBFrame::GetLineEntry () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBLineEntry sb_line_entry; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetLineEntry () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetLineEntry () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetLineEntry () => SBLineEntry(%p)", + frame, sb_line_entry.get()); + return sb_line_entry; +} + +uint32_t +SBFrame::GetFrameID () const +{ + uint32_t frame_idx = UINT32_MAX; + + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + if (frame) + frame_idx = frame->GetFrameIndex (); + + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBFrame(%p)::GetFrameID () => %u", + frame, frame_idx); + return frame_idx; +} + +addr_t +SBFrame::GetPC () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + addr_t addr = LLDB_INVALID_ADDRESS; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::GetPC () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetPC () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetPC () => 0x%" PRIx64, frame, addr); + + return addr; +} + +bool +SBFrame::SetPC (addr_t new_pc) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool ret_val = false; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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); + } + else + { + if (log) + log->Printf ("SBFrame::SetPC () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::SetPC () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::SetPC (new_pc=0x%" PRIx64 ") => %i", + frame, new_pc, ret_val); + + return ret_val; +} + +addr_t +SBFrame::GetSP () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + addr_t addr = LLDB_INVALID_ADDRESS; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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(); + } + else + { + if (log) + log->Printf ("SBFrame::GetSP () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetSP () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetSP () => 0x%" PRIx64, frame, addr); + + return addr; +} + + +addr_t +SBFrame::GetFP () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + addr_t addr = LLDB_INVALID_ADDRESS; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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(); + } + else + { + if (log) + log->Printf ("SBFrame::GetFP () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFP () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetFP () => 0x%" PRIx64, frame, addr); + return addr; +} + + +SBAddress +SBFrame::GetPCAddress () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBAddress sb_addr; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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()); + } + else + { + if (log) + log->Printf ("SBFrame::GetPCAddress () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetPCAddress () => error: process is running"); + } + } + if (log) + log->Printf ("SBFrame(%p)::GetPCAddress () => SBAddress(%p)", frame, sb_addr.get()); + return sb_addr; +} + +void +SBFrame::Clear() +{ + m_opaque_sp->Clear(); +} + +lldb::SBValue +SBFrame::GetValueForVariablePath (const char *var_path) +{ + SBValue sb_value; + ExecutionContext exe_ctx(m_opaque_sp.get()); + 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 sb_value; +} + +lldb::SBValue +SBFrame::GetValueForVariablePath (const char *var_path, DynamicValueType use_dynamic) +{ + SBValue sb_value; + Mutex::Locker api_locker; + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (var_path == NULL || var_path[0] == '\0') + { + if (log) + log->Printf ("SBFrame::GetValueForVariablePath called with empty variable path."); + return sb_value; + } + + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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; + Error error; + ValueObjectSP value_sp (frame->GetValueForVariableExpressionPath (var_path, + eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember | StackFrame::eExpressionPathOptionsAllowDirectIVarAccess, + var_sp, + error)); + sb_value.SetSP(value_sp, use_dynamic); + } + else + { + if (log) + log->Printf ("SBFrame::GetValueForVariablePath () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetValueForVariablePath () => error: process is running"); + } + } + return sb_value; +} + +SBValue +SBFrame::FindVariable (const char *name) +{ + SBValue value; + ExecutionContext exe_ctx(m_opaque_sp.get()); + 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 value; +} + + +SBValue +SBFrame::FindVariable (const char *name, lldb::DynamicValueType use_dynamic) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + VariableSP var_sp; + SBValue sb_value; + + if (name == NULL || name[0] == '\0') + { + if (log) + log->Printf ("SBFrame::FindVariable called with empty name"); + return sb_value; + } + + ValueObjectSP value_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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; + SymbolContext sc (frame->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, + &variable_list)) + { + var_sp = variable_list.FindVariable (ConstString(name)); + } + } + + if (var_sp) + { + value_sp = frame->GetValueObjectForFrameVariable(var_sp, eNoDynamicValues); + sb_value.SetSP(value_sp, use_dynamic); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindVariable () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindVariable () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::FindVariable (name=\"%s\") => SBValue(%p)", + frame, name, value_sp.get()); + + return sb_value; +} + +SBValue +SBFrame::FindValue (const char *name, ValueType value_type) +{ + SBValue value; + ExecutionContext exe_ctx(m_opaque_sp.get()); + 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 value; +} + +SBValue +SBFrame::FindValue (const char *name, ValueType value_type, lldb::DynamicValueType use_dynamic) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBValue sb_value; + + if (name == NULL || name[0] == '\0') + { + if (log) + log->Printf ("SBFrame::FindValue called with empty name."); + return sb_value; + } + + ValueObjectSP value_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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) + { + switch (value_type) + { + case eValueTypeVariableGlobal: // global variable + case eValueTypeVariableStatic: // static variable + case eValueTypeVariableArgument: // function argument variables + case eValueTypeVariableLocal: // function local variables + { + VariableList *variable_list = frame->GetVariableList(true); + + 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, + variable_list)) + { + ConstString const_name(name); + const uint32_t num_variables = variable_list->GetSize(); + for (uint32_t i = 0; i < num_variables; ++i) + { + VariableSP variable_sp (variable_list->GetVariableAtIndex(i)); + if (variable_sp && + variable_sp->GetScope() == value_type && + variable_sp->GetName() == const_name) + { + value_sp = frame->GetValueObjectForFrameVariable (variable_sp, eNoDynamicValues); + sb_value.SetSP (value_sp, use_dynamic); + break; + } + } + } + } + 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); + ClangExpressionVariableSP expr_var_sp (target->GetPersistentVariables().GetVariable (const_name)); + if (expr_var_sp) + { + value_sp = expr_var_sp->GetValueObject(); + sb_value.SetSP (value_sp, use_dynamic); + } + } + break; + + default: + break; + } + } + else + { + if (log) + log->Printf ("SBFrame::FindValue () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindValue () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::FindVariableInScope (name=\"%s\", value_type=%i) => SBValue(%p)", + frame, name, value_type, value_sp.get()); + + + return sb_value; +} + +bool +SBFrame::IsEqual (const SBFrame &that) const +{ + 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 +{ + return IsEqual(rhs); +} + +bool +SBFrame::operator != (const SBFrame &rhs) const +{ + return !IsEqual(rhs); +} + +SBThread +SBFrame::GetThread () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ExecutionContext exe_ctx(m_opaque_sp.get()); + ThreadSP thread_sp (exe_ctx.GetThreadSP()); + SBThread sb_thread (thread_sp); + + if (log) + { + SBStream sstr; + sb_thread.GetDescription (sstr); + log->Printf ("SBFrame(%p)::GetThread () => SBThread(%p): %s", + exe_ctx.GetFramePtr(), + thread_sp.get(), + sstr.GetData()); + } + + return sb_thread; +} + +const char * +SBFrame::Disassemble () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *disassembly = NULL; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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(); + } + else + { + if (log) + log->Printf ("SBFrame::Disassemble () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::Disassemble () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::Disassemble () => %s", frame, disassembly); + + return disassembly; +} + + +SBValueList +SBFrame::GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only) +{ + SBValueList value_list; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + if (frame && target) + { + lldb::DynamicValueType use_dynamic = frame->CalculateTarget()->GetPreferDynamicValue(); + value_list = GetVariables (arguments, locals, statics, in_scope_only, use_dynamic); + } + return value_list; +} + +SBValueList +SBFrame::GetVariables (bool arguments, + bool locals, + bool statics, + bool in_scope_only, + lldb::DynamicValueType use_dynamic) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBValueList value_list; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + Target *target = exe_ctx.GetTargetPtr(); + + if (log) + log->Printf ("SBFrame::GetVariables (arguments=%i, locals=%i, statics=%i, in_scope_only=%i)", + arguments, + locals, + statics, + in_scope_only); + + 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 = NULL; + 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: + add_variable = statics; + break; + + case eValueTypeVariableArgument: + add_variable = arguments; + break; + + case eValueTypeVariableLocal: + add_variable = locals; + break; + + default: + break; + } + if (add_variable) + { + if (in_scope_only && !variable_sp->IsInScope(frame)) + continue; + + ValueObjectSP valobj_sp(frame->GetValueObjectForFrameVariable (variable_sp, eNoDynamicValues)); + SBValue value_sb; + value_sb.SetSP(valobj_sp,use_dynamic); + value_list.Append(value_sb); + } + } + } + } + } + } + else + { + if (log) + log->Printf ("SBFrame::GetVariables () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetVariables () => error: process is running"); + } + } + + if (log) + { + log->Printf ("SBFrame(%p)::GetVariables (...) => SBValueList(%p)", frame, value_list.opaque_ptr()); + } + + return value_list; +} + +SBValueList +SBFrame::GetRegisters () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBValueList value_list; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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)); + } + } + } + else + { + if (log) + log->Printf ("SBFrame::GetRegisters () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetRegisters () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::GetRegisters () => SBValueList(%p)", frame, value_list.opaque_ptr()); + + return value_list; +} + +SBValue +SBFrame::FindRegister (const char *name) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBValue result; + ValueObjectSP value_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrame *frame = NULL; + 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; + } + } + } + } + else + { + if (log) + log->Printf ("SBFrame::FindRegister () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::FindRegister () => error: process is running"); + } + } + + if (log) + log->Printf ("SBFrame(%p)::FindRegister () => SBValue(%p)", frame, value_sp.get()); + + return result; +} + +bool +SBFrame::GetDescription (SBStream &description) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + Stream &strm = description.ref(); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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 + { + if (log) + log->Printf ("SBFrame::GetDescription () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetDescription () => error: process is running"); + } + + } + else + strm.PutCString ("No value"); + + return true; +} + +SBValue +SBFrame::EvaluateExpression (const char *expr) +{ + SBValue result; + ExecutionContext exe_ctx(m_opaque_sp.get()); + 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); + return EvaluateExpression (expr, options); + } + return result; +} + +SBValue +SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value) +{ + SBExpressionOptions options; + options.SetFetchDynamicValue (fetch_dynamic_value); + options.SetUnwindOnError (true); + return EvaluateExpression (expr, options); +} + +SBValue +SBFrame::EvaluateExpression (const char *expr, lldb::DynamicValueType fetch_dynamic_value, bool unwind_on_error) +{ + SBExpressionOptions options; + options.SetFetchDynamicValue (fetch_dynamic_value); + options.SetUnwindOnError (unwind_on_error); + return EvaluateExpression (expr, options); +} + +lldb::SBValue +SBFrame::EvaluateExpression (const char *expr, const SBExpressionOptions &options) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Log *expr_log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ExecutionResults exe_results = eExecutionSetupError; + SBValue expr_result; + + if (expr == NULL || expr[0] == '\0') + { + if (log) + log->Printf ("SBFrame::EvaluateExpression called with an empty expression"); + return expr_result; + } + + ValueObjectSP expr_value_sp; + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (log) + log->Printf ("SBFrame()::EvaluateExpression (expr=\"%s\")...", expr); + + StackFrame *frame = NULL; + 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) + { +#ifdef LLDB_CONFIGURATION_DEBUG + StreamString frame_description; + frame->DumpUsingSettingsFormat (&frame_description); + Host::SetCrashDescriptionWithFormat ("SBFrame::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", + expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); +#endif + exe_results = target->EvaluateExpression (expr, + frame, + expr_value_sp, + options.ref()); + expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); +#ifdef LLDB_CONFIGURATION_DEBUG + Host::SetCrashDescription (NULL); +#endif + } + else + { + if (log) + log->Printf ("SBFrame::EvaluateExpression () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::EvaluateExpression () => error: process is running"); + } + } + +#ifndef LLDB_DISABLE_PYTHON + if (expr_log) + expr_log->Printf("** [SBFrame::EvaluateExpression] Expression result is %s, summary %s **", + expr_result.GetValue(), + expr_result.GetSummary()); + + if (log) + log->Printf ("SBFrame(%p)::EvaluateExpression (expr=\"%s\") => SBValue(%p) (execution result=%d)", + frame, + expr, + expr_value_sp.get(), + exe_results); +#endif + + return expr_result; +} + +bool +SBFrame::IsInlined() +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = NULL; + 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 () != NULL; + } + else + { + if (log) + log->Printf ("SBFrame::IsInlined () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::IsInlined () => error: process is running"); + } + + } + return false; +} + +const char * +SBFrame::GetFunctionName() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *name = NULL; + ExecutionContext exe_ctx(m_opaque_sp.get()); + StackFrame *frame = NULL; + 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().AsCString(); + } + } + + if (name == NULL) + { + if (sc.function) + name = sc.function->GetName().GetCString(); + } + + if (name == NULL) + { + if (sc.symbol) + name = sc.symbol->GetName().GetCString(); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFunctionName () => error: could not reconstruct frame object for this SBFrame."); + } + } + else + { + if (log) + log->Printf ("SBFrame::GetFunctionName() => error: process is running"); + + } + } + return name; +} + diff --git a/source/API/SBFunction.cpp b/source/API/SBFunction.cpp new file mode 100644 index 00000000000..914d2d77f3e --- /dev/null +++ b/source/API/SBFunction.cpp @@ -0,0 +1,225 @@ +//===-- SBFunction.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBFunction.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SBFunction::SBFunction () : + m_opaque_ptr (NULL) +{ +} + +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) +{ +} + +const SBFunction & +SBFunction::operator = (const SBFunction &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBFunction::~SBFunction () +{ + m_opaque_ptr = NULL; +} + +bool +SBFunction::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +const char * +SBFunction::GetName() const +{ + const char *cstr = NULL; + if (m_opaque_ptr) + cstr = m_opaque_ptr->GetMangled().GetName().AsCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (cstr) + log->Printf ("SBFunction(%p)::GetName () => \"%s\"", m_opaque_ptr, cstr); + else + log->Printf ("SBFunction(%p)::GetName () => NULL", m_opaque_ptr); + } + return cstr; +} + +const char * +SBFunction::GetMangledName () const +{ + const char *cstr = NULL; + if (m_opaque_ptr) + cstr = m_opaque_ptr->GetMangled().GetMangledName().AsCString(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (cstr) + log->Printf ("SBFunction(%p)::GetMangledName () => \"%s\"", m_opaque_ptr, cstr); + else + log->Printf ("SBFunction(%p)::GetMangledName () => NULL", m_opaque_ptr); + } + return cstr; +} + +bool +SBFunction::operator == (const SBFunction &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; +} + +bool +SBFunction::operator != (const SBFunction &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +bool +SBFunction::GetDescription (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) +{ + return GetInstructions (target, NULL); +} + +SBInstructionList +SBFunction::GetInstructions (SBTarget target, const char *flavor) +{ + SBInstructionList sb_instructions; + if (m_opaque_ptr) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (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) + { + sb_instructions.SetDisassembler (Disassembler::DisassembleRange (module_sp->GetArchitecture(), + NULL, + flavor, + exe_ctx, + m_opaque_ptr->GetAddressRange())); + } + } + return 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 () +{ + SBAddress addr; + if (m_opaque_ptr) + addr.SetAddress (&m_opaque_ptr->GetAddressRange().GetBaseAddress()); + return addr; +} + +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 addr; +} + + +uint32_t +SBFunction::GetPrologueByteSize () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetPrologueByteSize(); + return 0; +} + +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 sb_type; +} + +SBBlock +SBFunction::GetBlock () +{ + SBBlock sb_block; + if (m_opaque_ptr) + sb_block.SetPtr (&m_opaque_ptr->GetBlock (true)); + return sb_block; +} + + + diff --git a/source/API/SBHostOS.cpp b/source/API/SBHostOS.cpp new file mode 100644 index 00000000000..a8f7db90a15 --- /dev/null +++ b/source/API/SBHostOS.cpp @@ -0,0 +1,85 @@ +//===-- SBHostOS.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBHostOS.h" +#include "lldb/API/SBError.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + + + +SBFileSpec +SBHostOS::GetProgramFileSpec () +{ + SBFileSpec sb_filespec; + sb_filespec.SetFileSpec (Host::GetProgramFileSpec ()); + return sb_filespec; +} + +SBFileSpec +SBHostOS::GetLLDBPythonPath () +{ + SBFileSpec sb_lldb_python_filespec; + FileSpec lldb_python_spec; + if (Host::GetLLDBPath (ePathTypePythonDir, lldb_python_spec)) + { + sb_lldb_python_filespec.SetFileSpec (lldb_python_spec); + } + return sb_lldb_python_filespec; +} + +lldb::thread_t +SBHostOS::ThreadCreate +( + const char *name, + void *(*thread_function)(void *), + void *thread_arg, + SBError *error_ptr +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBHostOS::ThreadCreate (name=\"%s\", thread_function=%p, thread_arg=%p, error_ptr=%p)", name, + thread_function, thread_arg, error_ptr); + + // FIXME: You should log the return value? + + return Host::ThreadCreate (name, thread_function, thread_arg, error_ptr ? error_ptr->get() : NULL); +} + +void +SBHostOS::ThreadCreated (const char *name) +{ + Host::ThreadCreated (name); +} + +bool +SBHostOS::ThreadCancel (lldb::thread_t thread, SBError *error_ptr) +{ + return Host::ThreadCancel (thread, error_ptr ? error_ptr->get() : NULL); +} + +bool +SBHostOS::ThreadDetach (lldb::thread_t thread, SBError *error_ptr) +{ + return Host::ThreadDetach (thread, error_ptr ? error_ptr->get() : NULL); +} + +bool +SBHostOS::ThreadJoin (lldb::thread_t thread, void **result, SBError *error_ptr) +{ + return Host::ThreadJoin (thread, result, error_ptr ? error_ptr->get() : NULL); +} + + diff --git a/source/API/SBInputReader.cpp b/source/API/SBInputReader.cpp new file mode 100644 index 00000000000..82b75c869f0 --- /dev/null +++ b/source/API/SBInputReader.cpp @@ -0,0 +1,216 @@ +//===-- SBInputReader.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/lldb-enumerations.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBInputReader.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Log.h" + + +using namespace lldb; +using namespace lldb_private; + +SBInputReader::SBInputReader () : + m_opaque_sp (), + m_callback_function (NULL), + m_callback_baton (NULL) + +{ +} + +SBInputReader::SBInputReader (const lldb::InputReaderSP &reader_sp) : + m_opaque_sp (reader_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBInputReader::SBInputReader (reader_sp=%p) => SBInputReader(%p)", reader_sp.get(), + m_opaque_sp.get()); +} + +SBInputReader::SBInputReader (const SBInputReader &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf("SBInputReader::SBInputReader (rhs.sp=%p) => SBInputReader(%p)", + rhs.m_opaque_sp.get(), m_opaque_sp.get()); +} + +SBInputReader::~SBInputReader () +{ +} + +size_t +SBInputReader::PrivateCallback +( + void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + SBInputReader *sb_reader = (SBInputReader *)baton; + return sb_reader->m_callback_function (sb_reader->m_callback_baton, + sb_reader, + notification, + bytes, + bytes_len); +} + +SBError +SBInputReader::Initialize +( + SBDebugger &debugger, + Callback callback_function, + void *callback_baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf("SBInputReader(%p)::Initialize (SBDebugger(%p), callback_function=%p, callback_baton=%p, " + "granularity=%s, end_token=\"%s\", prompt=\"%s\", echo=%i)", + m_opaque_sp.get(), + debugger.get(), + callback_function, + callback_baton, + InputReader::GranularityAsCString (granularity), end_token, prompt, + echo); + + SBError sb_error; + m_opaque_sp.reset (new InputReader (debugger.ref())); + + m_callback_function = callback_function; + m_callback_baton = callback_baton; + + if (m_opaque_sp) + { + sb_error.SetError (m_opaque_sp->Initialize (SBInputReader::PrivateCallback, + this, + granularity, + end_token, + prompt, + echo)); + } + + if (sb_error.Fail()) + { + m_opaque_sp.reset (); + m_callback_function = NULL; + m_callback_baton = NULL; + } + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBInputReader(%p)::Initialize (...) => SBError(%p): %s", m_opaque_sp.get(), + sb_error.get(), sstr.GetData()); + } + + return sb_error; +} + +bool +SBInputReader::IsValid () const +{ + return (m_opaque_sp.get() != NULL); +} + +const SBInputReader & +SBInputReader::operator = (const SBInputReader &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +InputReader * +SBInputReader::operator->() const +{ + return m_opaque_sp.get(); +} + +lldb::InputReaderSP & +SBInputReader::operator *() +{ + return m_opaque_sp; +} + +const lldb::InputReaderSP & +SBInputReader::operator *() const +{ + return m_opaque_sp; +} + +InputReader * +SBInputReader::get() const +{ + return m_opaque_sp.get(); +} + +InputReader & +SBInputReader::ref() const +{ + assert (m_opaque_sp.get()); + return *m_opaque_sp; +} + +bool +SBInputReader::IsDone () const +{ + if (m_opaque_sp) + return m_opaque_sp->IsDone(); + else + return true; +} + +void +SBInputReader::SetIsDone (bool value) +{ + if (m_opaque_sp) + m_opaque_sp->SetIsDone (value); +} + +bool +SBInputReader::IsActive () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_value = false; + if (m_opaque_sp) + ret_value = m_opaque_sp->IsActive(); + + if (log) + log->Printf ("SBInputReader(%p)::IsActive () => %i", m_opaque_sp.get(), ret_value); + + return ret_value; +} + +InputReaderGranularity +SBInputReader::GetGranularity () +{ + if (m_opaque_sp) + return m_opaque_sp->GetGranularity(); + else + return eInputReaderGranularityInvalid; +} diff --git a/source/API/SBInstruction.cpp b/source/API/SBInstruction.cpp new file mode 100644 index 00000000000..2334cc0d124 --- /dev/null +++ b/source/API/SBInstruction.cpp @@ -0,0 +1,248 @@ +//===-- SBInstruction.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBInstruction.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/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +SBInstruction::SBInstruction () +{ +} + +SBInstruction::SBInstruction (const lldb::InstructionSP& inst_sp) : + m_opaque_sp (inst_sp) +{ +} + +SBInstruction::SBInstruction(const SBInstruction &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBInstruction & +SBInstruction::operator = (const SBInstruction &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBInstruction::~SBInstruction () +{ +} + +bool +SBInstruction::IsValid() +{ + return (m_opaque_sp.get() != NULL); +} + +SBAddress +SBInstruction::GetAddress() +{ + SBAddress sb_addr; + if (m_opaque_sp && m_opaque_sp->GetAddress().IsValid()) + sb_addr.SetAddress(&m_opaque_sp->GetAddress()); + return sb_addr; +} + +const char * +SBInstruction::GetMnemonic(SBTarget target) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + return m_opaque_sp->GetMnemonic(&exe_ctx); + } + return NULL; +} + +const char * +SBInstruction::GetOperands(SBTarget target) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + return m_opaque_sp->GetOperands(&exe_ctx); + } + return NULL; +} + +const char * +SBInstruction::GetComment(SBTarget target) +{ + if (m_opaque_sp) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + exe_ctx.SetProcessSP(target_sp->GetProcessSP()); + } + return m_opaque_sp->GetComment(&exe_ctx); + } + return NULL; +} + +size_t +SBInstruction::GetByteSize () +{ + if (m_opaque_sp) + return m_opaque_sp->GetOpcode().GetByteSize(); + return 0; +} + +SBData +SBInstruction::GetData (SBTarget target) +{ + lldb::SBData sb_data; + if (m_opaque_sp) + { + DataExtractorSP data_extractor_sp (new DataExtractor()); + if (m_opaque_sp->GetData (*data_extractor_sp)) + { + sb_data.SetOpaque (data_extractor_sp); + } + } + return sb_data; +} + + + +bool +SBInstruction::DoesBranch () +{ + if (m_opaque_sp) + return m_opaque_sp->DoesBranch (); + return false; +} + +void +SBInstruction::SetOpaque (const lldb::InstructionSP &inst_sp) +{ + m_opaque_sp = inst_sp; +} + +bool +SBInstruction::GetDescription (lldb::SBStream &s) +{ + if (m_opaque_sp) + { + // Use the "ref()" instead of the "get()" accessor in case the SBStream + // didn't have a stream already created, one will get created... + m_opaque_sp->Dump (&s.ref(), 0, true, false, NULL); + return true; + } + return false; +} + +void +SBInstruction::Print (FILE *out) +{ + if (out == NULL) + return; + + if (m_opaque_sp) + { + StreamFile out_stream (out, false); + m_opaque_sp->Dump (&out_stream, 0, true, false, NULL); + } +} + +bool +SBInstruction::EmulateWithFrame (lldb::SBFrame &frame, uint32_t evaluate_options) +{ + if (m_opaque_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 m_opaque_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) +{ + if (m_opaque_sp && triple) + { + lldb_private::ArchSpec arch (triple, NULL); + + return m_opaque_sp->DumpEmulation (arch); + + } + return false; +} + +bool +SBInstruction::TestEmulation (lldb::SBStream &output_stream, const char *test_file) +{ + if (!m_opaque_sp.get()) + m_opaque_sp.reset (new PseudoInstruction()); + + return m_opaque_sp->TestEmulation (output_stream.get(), test_file); +} + +lldb::AddressClass +SBInstruction::GetAddressClass () +{ + if (m_opaque_sp.get()) + return m_opaque_sp->GetAddressClass(); + return eAddressClassInvalid; +} diff --git a/source/API/SBInstructionList.cpp b/source/API/SBInstructionList.cpp new file mode 100644 index 00000000000..fe22d9c29e4 --- /dev/null +++ b/source/API/SBInstructionList.cpp @@ -0,0 +1,132 @@ +//===-- SBInstructionList.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + + +SBInstructionList::SBInstructionList () : + m_opaque_sp() +{ +} + +SBInstructionList::SBInstructionList(const SBInstructionList &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBInstructionList & +SBInstructionList::operator = (const SBInstructionList &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + + +SBInstructionList::~SBInstructionList () +{ +} + +bool +SBInstructionList::IsValid () const +{ + return m_opaque_sp.get() != NULL; +} + +size_t +SBInstructionList::GetSize () +{ + if (m_opaque_sp) + return m_opaque_sp->GetInstructionList().GetSize(); + return 0; +} + +SBInstruction +SBInstructionList::GetInstructionAtIndex (uint32_t idx) +{ + SBInstruction inst; + if (m_opaque_sp && idx < m_opaque_sp->GetInstructionList().GetSize()) + inst.SetOpaque (m_opaque_sp->GetInstructionList().GetInstructionAtIndex (idx)); + return inst; +} + +void +SBInstructionList::Clear () +{ + m_opaque_sp.reset(); +} + +void +SBInstructionList::AppendInstruction (SBInstruction insn) +{ +} + +void +SBInstructionList::SetDisassembler (const lldb::DisassemblerSP &opaque_sp) +{ + m_opaque_sp = opaque_sp; +} + +void +SBInstructionList::Print (FILE *out) +{ + if (out == NULL) + return; +} + + +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(); + for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i).get(); + if (inst == NULL) + break; + inst->Dump (&sref, max_opcode_byte_size, true, false, NULL); + sref.EOL(); + } + return true; + } + } + return false; +} + + +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; +} + diff --git a/source/API/SBLineEntry.cpp b/source/API/SBLineEntry.cpp new file mode 100644 index 00000000000..0864a2e006c --- /dev/null +++ b/source/API/SBLineEntry.cpp @@ -0,0 +1,250 @@ +//===-- SBLineEntry.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Log.h" +#include "lldb/Symbol/LineEntry.h" + +using namespace lldb; +using namespace lldb_private; + + +SBLineEntry::SBLineEntry () : + m_opaque_ap () +{ +} + +SBLineEntry::SBLineEntry (const SBLineEntry &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + ref() = rhs.ref(); +} + +SBLineEntry::SBLineEntry (const lldb_private::LineEntry *lldb_object_ptr) : + m_opaque_ap () +{ + if (lldb_object_ptr) + ref() = *lldb_object_ptr; +} + +const SBLineEntry & +SBLineEntry::operator = (const SBLineEntry &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + ref() = rhs.ref(); + else + m_opaque_ap.reset(); + } + return *this; +} + +void +SBLineEntry::SetLineEntry (const lldb_private::LineEntry &lldb_object_ref) +{ + ref() = lldb_object_ref; +} + + +SBLineEntry::~SBLineEntry () +{ +} + + +SBAddress +SBLineEntry::GetStartAddress () const +{ + + SBAddress sb_address; + if (m_opaque_ap.get()) + sb_address.SetAddress(&m_opaque_ap->range.GetBaseAddress()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + StreamString sstr; + const Address *addr = sb_address.get(); + if (addr) + addr->Dump (&sstr, NULL, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleInvalid, 4); + log->Printf ("SBLineEntry(%p)::GetStartAddress () => SBAddress (%p): %s", + m_opaque_ap.get(), sb_address.get(), sstr.GetData()); + } + + return sb_address; +} + +SBAddress +SBLineEntry::GetEndAddress () const +{ + SBAddress sb_address; + if (m_opaque_ap.get()) + { + sb_address.SetAddress(&m_opaque_ap->range.GetBaseAddress()); + sb_address.OffsetAddress(m_opaque_ap->range.GetByteSize()); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + StreamString sstr; + const Address *addr = sb_address.get(); + if (addr) + addr->Dump (&sstr, NULL, Address::DumpStyleModuleWithFileAddress, Address::DumpStyleInvalid, 4); + log->Printf ("SBLineEntry(%p)::GetEndAddress () => SBAddress (%p): %s", + m_opaque_ap.get(), sb_address.get(), sstr.GetData()); + } + return sb_address; +} + +bool +SBLineEntry::IsValid () const +{ + return m_opaque_ap.get() && m_opaque_ap->IsValid(); +} + + +SBFileSpec +SBLineEntry::GetFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec sb_file_spec; + if (m_opaque_ap.get() && m_opaque_ap->file) + sb_file_spec.SetFileSpec(m_opaque_ap->file); + + if (log) + { + SBStream sstr; + sb_file_spec.GetDescription (sstr); + log->Printf ("SBLineEntry(%p)::GetFileSpec () => SBFileSpec(%p): %s", m_opaque_ap.get(), + sb_file_spec.get(), sstr.GetData()); + } + + return sb_file_spec; +} + +uint32_t +SBLineEntry::GetLine () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t line = 0; + if (m_opaque_ap.get()) + line = m_opaque_ap->line; + + if (log) + log->Printf ("SBLineEntry(%p)::GetLine () => %u", m_opaque_ap.get(), line); + + return line; +} + + +uint32_t +SBLineEntry::GetColumn () const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->column; + return 0; +} + +void +SBLineEntry::SetFileSpec (lldb::SBFileSpec filespec) +{ + if (filespec.IsValid()) + ref().file = filespec.ref(); + else + ref().file.Clear(); +} +void +SBLineEntry::SetLine (uint32_t line) +{ + ref().line = line; +} + +void +SBLineEntry::SetColumn (uint32_t column) +{ + ref().line = column; +} + + + +bool +SBLineEntry::operator == (const SBLineEntry &rhs) const +{ + lldb_private::LineEntry *lhs_ptr = m_opaque_ap.get(); + lldb_private::LineEntry *rhs_ptr = rhs.m_opaque_ap.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_private::LineEntry *lhs_ptr = m_opaque_ap.get(); + lldb_private::LineEntry *rhs_ptr = rhs.m_opaque_ap.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_ap.get(); +} + +lldb_private::LineEntry & +SBLineEntry::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new lldb_private::LineEntry ()); + return *m_opaque_ap; +} + +const lldb_private::LineEntry & +SBLineEntry::ref() const +{ + return *m_opaque_ap; +} + +bool +SBLineEntry::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + char file_path[PATH_MAX*2]; + m_opaque_ap->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_ap.get(); +} diff --git a/source/API/SBListener.cpp b/source/API/SBListener.cpp new file mode 100644 index 00000000000..2e67b4c24e8 --- /dev/null +++ b/source/API/SBListener.cpp @@ -0,0 +1,443 @@ +//===-- SBListener.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBListener.h" +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Listener.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/TimeValue.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBListener::SBListener () : + m_opaque_sp (), + m_opaque_ptr (NULL) +{ +} + +SBListener::SBListener (const char *name) : + m_opaque_sp (new Listener (name)), + m_opaque_ptr (NULL) +{ + m_opaque_ptr = m_opaque_sp.get(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBListener::SBListener (name=\"%s\") => SBListener(%p)", + name, m_opaque_ptr); +} + + +SBListener::SBListener (const SBListener &rhs) : + m_opaque_sp (rhs.m_opaque_sp), + m_opaque_ptr (rhs.m_opaque_ptr) +{ +} + +const lldb::SBListener & +SBListener::operator = (const lldb::SBListener &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + m_opaque_ptr = rhs.m_opaque_ptr; + } + return *this; +} + +SBListener::SBListener (Listener &listener) : + m_opaque_sp (), + m_opaque_ptr (&listener) +{ +} + +SBListener::~SBListener () +{ +} + +bool +SBListener::IsValid() const +{ + return m_opaque_ptr != NULL; +} + +void +SBListener::AddEvent (const SBEvent &event) +{ + EventSP &event_sp = event.GetSP (); + if (event_sp) + m_opaque_ptr->AddEvent (event_sp); +} + +void +SBListener::Clear () +{ + if (m_opaque_ptr) + m_opaque_ptr->Clear (); +} + +uint32_t +SBListener::StartListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask) +{ + if (m_opaque_ptr) + { + Debugger *lldb_debugger = debugger.get(); + if (!lldb_debugger) + return 0; + BroadcastEventSpec event_spec (ConstString (broadcaster_class), event_mask); + return m_opaque_ptr->StartListeningForEventSpec (*lldb_debugger, event_spec); + } + else + return 0; +} + +bool +SBListener::StopListeningForEventClass (SBDebugger &debugger, + const char *broadcaster_class, + uint32_t event_mask) +{ + if (m_opaque_ptr) + { + Debugger *lldb_debugger = debugger.get(); + if (!lldb_debugger) + return false; + BroadcastEventSpec event_spec (ConstString (broadcaster_class), event_mask); + return m_opaque_ptr->StopListeningForEventSpec (*lldb_debugger, event_spec); + } + else + return false; +} + +uint32_t +SBListener::StartListeningForEvents (const SBBroadcaster& broadcaster, uint32_t event_mask) +{ + uint32_t acquired_event_mask = 0; + if (m_opaque_ptr && broadcaster.IsValid()) + { + acquired_event_mask = m_opaque_ptr->StartListeningForEvents (broadcaster.get(), event_mask); + } + + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + StreamString sstr_requested; + StreamString sstr_acquired; + + Broadcaster *lldb_broadcaster = broadcaster.get(); + if (lldb_broadcaster) + { + const bool got_requested_names = lldb_broadcaster->GetEventNames (sstr_requested, event_mask, false); + const bool got_acquired_names = lldb_broadcaster->GetEventNames (sstr_acquired, acquired_event_mask, false); + log->Printf ("SBListener(%p)::StartListeneingForEvents (SBBroadcaster(%p): %s, event_mask=0x%8.8x%s%s%s) => 0x%8.8x%s%s%s", + m_opaque_ptr, + lldb_broadcaster, + lldb_broadcaster->GetBroadcasterName().GetCString(), + event_mask, + got_requested_names ? " (" : "", + sstr_requested.GetData(), + got_requested_names ? ")" : "", + acquired_event_mask, + got_acquired_names ? " (" : "", + sstr_acquired.GetData(), + got_acquired_names ? ")" : ""); + } + else + { + log->Printf ("SBListener(%p)::StartListeneingForEvents (SBBroadcaster(%p), event_mask=0x%8.8x) => 0x%8.8x", + m_opaque_ptr, + lldb_broadcaster, + event_mask, + acquired_event_mask); + + } + } + + return acquired_event_mask; +} + +bool +SBListener::StopListeningForEvents (const SBBroadcaster& broadcaster, uint32_t event_mask) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + return m_opaque_ptr->StopListeningForEvents (broadcaster.get(), event_mask); + } + return false; +} + +bool +SBListener::WaitForEvent (uint32_t timeout_secs, SBEvent &event) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (timeout_secs == UINT32_MAX) + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=INFINITE, SBEvent(%p))...", + m_opaque_ptr, event.get()); + } + else + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=%d, SBEvent(%p))...", + m_opaque_ptr, timeout_secs, event.get()); + } + } + bool success = false; + + if (m_opaque_ptr) + { + TimeValue time_value; + if (timeout_secs != UINT32_MAX) + { + assert (timeout_secs != 0); // Take this out after all calls with timeout set to zero have been removed.... + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (timeout_secs); + } + EventSP event_sp; + if (m_opaque_ptr->WaitForEvent (time_value.IsValid() ? &time_value : NULL, event_sp)) + { + event.reset (event_sp); + success = true; + } + } + + if (log) + { + if (timeout_secs == UINT32_MAX) + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=INFINITE, SBEvent(%p)) => %i", + m_opaque_ptr, event.get(), success); + } + else + { + log->Printf ("SBListener(%p)::WaitForEvent (timeout_secs=%d, SBEvent(%p)) => %i", + m_opaque_ptr, timeout_secs, event.get(), success); + } + } + if (!success) + event.reset (NULL); + return success; +} + +bool +SBListener::WaitForEventForBroadcaster +( + uint32_t num_seconds, + const SBBroadcaster &broadcaster, + SBEvent &event +) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_opaque_ptr->WaitForEventForBroadcaster (time_value.IsValid() ? &time_value : NULL, + broadcaster.get(), + event_sp)) + { + event.reset (event_sp); + return true; + } + + } + event.reset (NULL); + return false; +} + +bool +SBListener::WaitForEventForBroadcasterWithType +( + uint32_t num_seconds, + const SBBroadcaster &broadcaster, + uint32_t event_type_mask, + SBEvent &event +) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + TimeValue time_value; + if (num_seconds != UINT32_MAX) + { + time_value = TimeValue::Now(); + time_value.OffsetWithSeconds (num_seconds); + } + EventSP event_sp; + if (m_opaque_ptr->WaitForEventForBroadcasterWithType (time_value.IsValid() ? &time_value : NULL, + broadcaster.get(), + event_type_mask, + event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::PeekAtNextEvent (SBEvent &event) +{ + if (m_opaque_ptr) + { + event.reset (m_opaque_ptr->PeekAtNextEvent ()); + return event.IsValid(); + } + event.reset (NULL); + return false; +} + +bool +SBListener::PeekAtNextEventForBroadcaster (const SBBroadcaster &broadcaster, SBEvent &event) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + event.reset (m_opaque_ptr->PeekAtNextEventForBroadcaster (broadcaster.get())); + return event.IsValid(); + } + event.reset (NULL); + return false; +} + +bool +SBListener::PeekAtNextEventForBroadcasterWithType (const SBBroadcaster &broadcaster, uint32_t event_type_mask, + SBEvent &event) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + event.reset(m_opaque_ptr->PeekAtNextEventForBroadcasterWithType (broadcaster.get(), event_type_mask)); + return event.IsValid(); + } + event.reset (NULL); + return false; +} + +bool +SBListener::GetNextEvent (SBEvent &event) +{ + if (m_opaque_ptr) + { + EventSP event_sp; + if (m_opaque_ptr->GetNextEvent (event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::GetNextEventForBroadcaster (const SBBroadcaster &broadcaster, SBEvent &event) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + EventSP event_sp; + if (m_opaque_ptr->GetNextEventForBroadcaster (broadcaster.get(), event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::GetNextEventForBroadcasterWithType +( + const SBBroadcaster &broadcaster, + uint32_t event_type_mask, + SBEvent &event +) +{ + if (m_opaque_ptr && broadcaster.IsValid()) + { + EventSP event_sp; + if (m_opaque_ptr->GetNextEventForBroadcasterWithType (broadcaster.get(), + event_type_mask, + event_sp)) + { + event.reset (event_sp); + return true; + } + } + event.reset (NULL); + return false; +} + +bool +SBListener::HandleBroadcastEvent (const SBEvent &event) +{ + if (m_opaque_ptr) + return m_opaque_ptr->HandleBroadcastEvent (event.GetSP()); + return false; +} + +Listener * +SBListener::operator->() const +{ + return m_opaque_ptr; +} + +Listener * +SBListener::get() const +{ + return m_opaque_ptr; +} + +void +SBListener::reset(Listener *listener, bool owns) +{ + if (owns) + m_opaque_sp.reset (listener); + else + m_opaque_sp.reset (); + m_opaque_ptr = listener; +} + +Listener & +SBListener::ref() const +{ + return *m_opaque_ptr; +} + +Listener & +SBListener::operator *() +{ + return *m_opaque_ptr; +} + +const Listener & +SBListener::operator *() const +{ + return *m_opaque_ptr; +} + + diff --git a/source/API/SBModule.cpp b/source/API/SBModule.cpp new file mode 100644 index 00000000000..5f5fc9292cd --- /dev/null +++ b/source/API/SBModule.cpp @@ -0,0 +1,655 @@ +//===-- SBModule.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBModule.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/Log.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/Symtab.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBModule::SBModule () : + m_opaque_sp () +{ +} + +SBModule::SBModule (const lldb::ModuleSP& module_sp) : + m_opaque_sp (module_sp) +{ +} + +SBModule::SBModule(const SBModuleSpec &module_spec) : + m_opaque_sp () +{ + ModuleSP module_sp; + Error error = ModuleList::GetSharedModule (*module_spec.m_opaque_ap, + module_sp, + NULL, + NULL, + NULL); + if (module_sp) + SetSP(module_sp); +} + +SBModule::SBModule(const SBModule &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +SBModule::SBModule (lldb::SBProcess &process, lldb::addr_t header_addr) : + m_opaque_sp () +{ + 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, changed); + target.GetImages().Append(m_opaque_sp); + } + } +} + +const SBModule & +SBModule::operator = (const SBModule &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +SBModule::~SBModule () +{ +} + +bool +SBModule::IsValid () const +{ + return m_opaque_sp.get() != NULL; +} + +void +SBModule::Clear() +{ + m_opaque_sp.reset(); +} + +SBFileSpec +SBModule::GetFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec file_spec; + ModuleSP module_sp (GetSP ()); + if (module_sp) + file_spec.SetFileSpec(module_sp->GetFileSpec()); + + if (log) + { + log->Printf ("SBModule(%p)::GetFileSpec () => SBFileSpec(%p)", + module_sp.get(), file_spec.get()); + } + + return file_spec; +} + +lldb::SBFileSpec +SBModule::GetPlatformFileSpec () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFileSpec file_spec; + ModuleSP module_sp (GetSP ()); + if (module_sp) + file_spec.SetFileSpec(module_sp->GetPlatformFileSpec()); + + if (log) + { + log->Printf ("SBModule(%p)::GetPlatformFileSpec () => SBFileSpec(%p)", + module_sp.get(), file_spec.get()); + } + + return file_spec; + +} + +bool +SBModule::SetPlatformFileSpec (const lldb::SBFileSpec &platform_file) +{ + bool result = false; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + module_sp->SetPlatformFileSpec(*platform_file); + result = true; + } + + if (log) + { + log->Printf ("SBModule(%p)::SetPlatformFileSpec (SBFileSpec(%p (%s)) => %i", + module_sp.get(), + platform_file.get(), + platform_file->GetPath().c_str(), + result); + } + return result; +} + + + +const uint8_t * +SBModule::GetUUIDBytes () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const uint8_t *uuid_bytes = NULL; + ModuleSP module_sp (GetSP ()); + if (module_sp) + uuid_bytes = (const uint8_t *)module_sp->GetUUID().GetBytes(); + + if (log) + { + if (uuid_bytes) + { + StreamString s; + module_sp->GetUUID().Dump (&s); + log->Printf ("SBModule(%p)::GetUUIDBytes () => %s", module_sp.get(), s.GetData()); + } + else + log->Printf ("SBModule(%p)::GetUUIDBytes () => NULL", module_sp.get()); + } + return uuid_bytes; +} + + +const char * +SBModule::GetUUIDString () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + static char uuid_string_buffer[80]; + const char *uuid_c_string = NULL; + std::string uuid_string; + ModuleSP module_sp (GetSP ()); + if (module_sp) + uuid_string = module_sp->GetUUID().GetAsString(); + + if (!uuid_string.empty()) + { + strncpy (uuid_string_buffer, uuid_string.c_str(), sizeof (uuid_string_buffer)); + uuid_c_string = uuid_string_buffer; + } + + if (log) + { + if (!uuid_string.empty()) + { + StreamString s; + module_sp->GetUUID().Dump (&s); + log->Printf ("SBModule(%p)::GetUUIDString () => %s", module_sp.get(), s.GetData()); + } + else + log->Printf ("SBModule(%p)::GetUUIDString () => NULL", module_sp.get()); + } + return uuid_c_string; +} + + +bool +SBModule::operator == (const SBModule &rhs) const +{ + if (m_opaque_sp) + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); + return false; +} + +bool +SBModule::operator != (const SBModule &rhs) const +{ + 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::SBAddress sb_addr; + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + Address addr; + if (module_sp->ResolveFileAddress (vm_addr, addr)) + sb_addr.ref() = addr; + } + return sb_addr; +} + +SBSymbolContext +SBModule::ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope) +{ + SBSymbolContext sb_sc; + ModuleSP module_sp (GetSP ()); + if (module_sp && addr.IsValid()) + module_sp->ResolveSymbolContextForAddress (addr.ref(), resolve_scope, *sb_sc); + return sb_sc; +} + +bool +SBModule::GetDescription (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() +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + return module_sp->GetNumCompileUnits (); + } + return 0; +} + +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 sb_cu; +} + +static Symtab * +GetUnifiedSymbolTable (const lldb::ModuleSP& module_sp) +{ + if (module_sp) + { + SymbolVendor *symbols = module_sp->GetSymbolVendor(); + if (symbols) + return symbols->GetSymtab(); + } + return NULL; +} + +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) +{ + SBSymbol sb_symbol; + ModuleSP module_sp (GetSP ()); + Symtab *symtab = GetUnifiedSymbolTable (module_sp); + if (symtab) + sb_symbol.SetSymbol(symtab->SymbolAtIndex (idx)); + return sb_symbol; +} + +lldb::SBSymbol +SBModule::FindSymbol (const char *name, + lldb::SymbolType 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 sb_symbol; +} + + +lldb::SBSymbolContextList +SBModule::FindSymbols (const char *name, lldb::SymbolType symbol_type) +{ + SBSymbolContextList sb_sc_list; + if (name && name[0]) + { + ModuleSP module_sp (GetSP ()); + Symtab *symtab = GetUnifiedSymbolTable (module_sp); + if (symtab) + { + std::vector 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; iSymbolAtIndex (matching_symbol_indexes[i]); + if (sc.symbol) + sc_list.Append(sc); + } + } + } + } + return sb_sc_list; + +} + + + +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) +{ + 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 sb_section; +} + +lldb::SBSymbolContextList +SBModule::FindFunctions (const char *name, + uint32_t 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; + module_sp->FindFunctions (ConstString(name), + NULL, + name_type_mask, + symbols_ok, + inlines_ok, + append, + *sb_sc_list); + } + return sb_sc_list; +} + + +SBValueList +SBModule::FindGlobalVariables (SBTarget &target, const char *name, uint32_t 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), + NULL, + false, + max_matches, + variable_list); + + if (match_count > 0) + { + for (uint32_t i=0; i 0) + return sb_value_list.GetValueAtIndex(0); + return SBValue(); +} + +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()) + sb_type = SBType (ClangASTContext::GetBasicType (module_sp->GetClangASTContext().getASTContext(), name)); + } + return sb_type; +} + +lldb::SBType +SBModule::GetBasicType(lldb::BasicType type) +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return SBType (ClangASTContext::GetBasicType (module_sp->GetClangASTContext().getASTContext(), type)); + return SBType(); +} + +lldb::SBTypeList +SBModule::FindTypes (const char *type) +{ + SBTypeList retval; + + ModuleSP module_sp (GetSP ()); + if (type && module_sp) + { + SymbolContext sc; + TypeList type_list; + const bool exact_match = false; + ConstString name(type); + const uint32_t num_matches = module_sp->FindTypes (sc, + name, + exact_match, + UINT32_MAX, + 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 + { + SBType sb_type(ClangASTContext::GetBasicType (module_sp->GetClangASTContext().getASTContext(), name)); + if (sb_type.IsValid()) + retval.Append(sb_type); + } + } + + return retval; +} + +lldb::SBTypeList +SBModule::GetTypes (uint32_t type_mask) +{ + SBTypeList sb_type_list; + + ModuleSP module_sp (GetSP ()); + if (module_sp) + { + SymbolVendor* vendor = module_sp->GetSymbolVendor(); + if (vendor) + { + TypeList type_list; + vendor->GetTypes (NULL, type_mask, type_list); + sb_type_list.m_opaque_ap->Append(type_list); + } + } + return sb_type_list; +} + +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 sb_section; +} + +lldb::ByteOrder +SBModule::GetByteOrder () +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return module_sp->GetArchitecture().GetByteOrder(); + return eByteOrderInvalid; +} + +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 NULL; +} + +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) +{ + ModuleSP module_sp (GetSP ()); + if (module_sp) + return module_sp->GetVersion(versions, num_versions); + else + { + if (versions && num_versions) + { + for (uint32_t i=0; iClear(); +} + +SBFileSpec +SBModuleSpec::GetFileSpec () +{ + SBFileSpec sb_spec(m_opaque_ap->GetFileSpec()); + return sb_spec; +} + +void +SBModuleSpec::SetFileSpec (const lldb::SBFileSpec &sb_spec) +{ + m_opaque_ap->GetFileSpec() = *sb_spec; +} + +lldb::SBFileSpec +SBModuleSpec::GetPlatformFileSpec () +{ + return SBFileSpec(m_opaque_ap->GetPlatformFileSpec()); +} + +void +SBModuleSpec::SetPlatformFileSpec (const lldb::SBFileSpec &sb_spec) +{ + m_opaque_ap->GetPlatformFileSpec() = *sb_spec; +} + +lldb::SBFileSpec +SBModuleSpec::GetSymbolFileSpec () +{ + return SBFileSpec(m_opaque_ap->GetSymbolFileSpec()); +} + +void +SBModuleSpec::SetSymbolFileSpec (const lldb::SBFileSpec &sb_spec) +{ + m_opaque_ap->GetSymbolFileSpec() = *sb_spec; +} + +const char * +SBModuleSpec::GetObjectName () +{ + return m_opaque_ap->GetObjectName().GetCString(); +} + +void +SBModuleSpec::SetObjectName (const char *name) +{ + m_opaque_ap->GetObjectName().SetCString(name); +} + +const char * +SBModuleSpec::GetTriple () +{ + std::string triple (m_opaque_ap->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) +{ + m_opaque_ap->GetArchitecture().SetTriple(triple); +} + +const uint8_t * +SBModuleSpec::GetUUIDBytes () +{ + return (const uint8_t *)m_opaque_ap->GetUUID().GetBytes(); +} + +size_t +SBModuleSpec::GetUUIDLength () +{ + return m_opaque_ap->GetUUID().GetByteSize(); +} + +bool +SBModuleSpec::SetUUIDBytes (const uint8_t *uuid, size_t uuid_len) +{ + return m_opaque_ap->GetUUID().SetBytes(uuid, uuid_len); +} + +bool +SBModuleSpec::GetDescription (lldb::SBStream &description) +{ + m_opaque_ap->Dump (description.ref()); + return true; +} + +SBModuleSpecList::SBModuleSpecList() : + m_opaque_ap(new ModuleSpecList()) +{ + +} + +SBModuleSpecList::SBModuleSpecList (const SBModuleSpecList &rhs) : + m_opaque_ap(new ModuleSpecList(*rhs.m_opaque_ap)) +{ + +} + +SBModuleSpecList & +SBModuleSpecList::operator = (const SBModuleSpecList &rhs) +{ + if (this != &rhs) + *m_opaque_ap = *rhs.m_opaque_ap; + return *this; +} + +SBModuleSpecList::~SBModuleSpecList() +{ + +} + +SBModuleSpecList +SBModuleSpecList::GetModuleSpecifications (const char *path) +{ + SBModuleSpecList specs; + FileSpec file_spec(path, true); + Host::ResolveExecutableInBundle(file_spec); + ObjectFile::GetModuleSpecifications(file_spec, 0, 0, *specs.m_opaque_ap); + return specs; +} + +void +SBModuleSpecList::Append (const SBModuleSpec &spec) +{ + m_opaque_ap->Append (*spec.m_opaque_ap); +} + +void +SBModuleSpecList::Append (const SBModuleSpecList &spec_list) +{ + m_opaque_ap->Append (*spec_list.m_opaque_ap); +} + +size_t +SBModuleSpecList::GetSize() +{ + return m_opaque_ap->GetSize(); +} + +SBModuleSpec +SBModuleSpecList::GetSpecAtIndex (size_t i) +{ + SBModuleSpec sb_module_spec; + m_opaque_ap->GetModuleSpecAtIndex(i, *sb_module_spec.m_opaque_ap); + return sb_module_spec; +} + +SBModuleSpec +SBModuleSpecList::FindFirstMatchingSpec (const SBModuleSpec &match_spec) +{ + SBModuleSpec sb_module_spec; + m_opaque_ap->FindMatchingModuleSpec(*match_spec.m_opaque_ap, *sb_module_spec.m_opaque_ap); + return sb_module_spec; +} + +SBModuleSpecList +SBModuleSpecList::FindMatchingSpecs (const SBModuleSpec &match_spec) +{ + SBModuleSpecList specs; + m_opaque_ap->FindMatchingModuleSpecs(*match_spec.m_opaque_ap, *specs.m_opaque_ap); + return specs; + +} + +bool +SBModuleSpecList::GetDescription (lldb::SBStream &description) +{ + m_opaque_ap->Dump (description.ref()); + return true; +} diff --git a/source/API/SBProcess.cpp b/source/API/SBProcess.cpp new file mode 100644 index 00000000000..259eb5e9703 --- /dev/null +++ b/source/API/SBProcess.cpp @@ -0,0 +1,1256 @@ +//===-- SBProcess.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBProcess.h" + +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +// Project includes + +#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/SBThread.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBStringList.h" + +using namespace lldb; +using namespace lldb_private; + + +SBProcess::SBProcess () : + m_opaque_wp() +{ +} + + +//---------------------------------------------------------------------- +// SBProcess constructor +//---------------------------------------------------------------------- + +SBProcess::SBProcess (const SBProcess& rhs) : + m_opaque_wp (rhs.m_opaque_wp) +{ +} + + +SBProcess::SBProcess (const lldb::ProcessSP &process_sp) : + m_opaque_wp (process_sp) +{ +} + +const SBProcess& +SBProcess::operator = (const SBProcess& rhs) +{ + if (this != &rhs) + m_opaque_wp = rhs.m_opaque_wp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBProcess::~SBProcess() +{ +} + +const char * +SBProcess::GetBroadcasterClassName () +{ + return Process::GetStaticBroadcasterClass().AsCString(); +} + +const char * +SBProcess::GetPluginName () +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + return process_sp->GetPluginName().GetCString(); + } + return ""; +} + +const char * +SBProcess::GetShortPluginName () +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + return process_sp->GetPluginName().GetCString(); + } + return ""; +} + + +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 () +{ + m_opaque_wp.reset(); +} + + +bool +SBProcess::IsValid() const +{ + 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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) { + log->Printf ("SBProcess(%p)::RemoteLaunch (argv=%p, envp=%p, stdin=%s, stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, stop_at_entry=%i, &error (%p))...", + m_opaque_wp.lock().get(), + argv, + envp, + stdin_path ? stdin_path : "NULL", + stdout_path ? stdout_path : "NULL", + stderr_path ? stderr_path : "NULL", + working_directory ? working_directory : "NULL", + launch_flags, + stop_at_entry, + error.get()); + } + + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + if (process_sp->GetState() == eStateConnected) + { + if (stop_at_entry) + launch_flags |= eLaunchFlagStopAtEntry; + ProcessLaunchInfo launch_info (stdin_path, + stdout_path, + stderr_path, + 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.GetEnvironmentEntries ().SetArguments (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"); + } + + if (log) { + SBStream sstr; + error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::RemoteLaunch (...) => SBError (%p): %s", process_sp.get(), error.get(), sstr.GetData()); + } + + return error.Success(); +} + +bool +SBProcess::RemoteAttachToProcessWithID (lldb::pid_t pid, lldb::SBError& error) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (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"); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) { + SBStream sstr; + error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::RemoteAttachToProcessWithID (%" PRIu64 ") => SBError (%p): %s", process_sp.get(), pid, error.get(), sstr.GetData()); + } + + return error.Success(); +} + + +uint32_t +SBProcess::GetNumThreads () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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()); + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + num_threads = process_sp->GetThreadList().GetSize(can_update); + } + + if (log) + log->Printf ("SBProcess(%p)::GetNumThreads () => %d", process_sp.get(), num_threads); + + return num_threads; +} + +SBThread +SBProcess::GetSelectedThread () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + thread_sp = process_sp->GetThreadList().GetSelectedThread(); + sb_thread.SetThread (thread_sp); + } + + if (log) + { + log->Printf ("SBProcess(%p)::GetSelectedThread () => SBThread(%p)", process_sp.get(), thread_sp.get()); + } + + return sb_thread; +} + +SBThread +SBProcess::CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + thread_sp = process_sp->CreateOSPluginThread(tid, context); + sb_thread.SetThread (thread_sp); + } + + if (log) + log->Printf ("SBProcess(%p)::CreateOSPluginThread (tid=0x%" PRIx64 ", context=0x%" PRIx64 ") => SBThread(%p)", process_sp.get(), tid, context, thread_sp.get()); + + return sb_thread; +} + +SBTarget +SBProcess::GetTarget() const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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); + } + + if (log) + log->Printf ("SBProcess(%p)::GetTarget () => SBTarget(%p)", process_sp.get(), target_sp.get()); + + return sb_target; +} + + +size_t +SBProcess::PutSTDIN (const char *src, size_t src_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + size_t ret_val = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + ret_val = process_sp->PutSTDIN (src, src_len, error); + } + + if (log) + log->Printf ("SBProcess(%p)::PutSTDIN (src=\"%s\", src_len=%d) => %lu", + process_sp.get(), + src, + (uint32_t) src_len, + ret_val); + + return ret_val; +} + +size_t +SBProcess::GetSTDOUT (char *dst, size_t dst_len) const +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + bytes_read = process_sp->GetSTDOUT (dst, dst_len, error); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetSTDOUT (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, + process_sp.get(), + (int) bytes_read, + dst, + (uint64_t)dst_len, + (uint64_t)bytes_read); + + return bytes_read; +} + +size_t +SBProcess::GetSTDERR (char *dst, size_t dst_len) const +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + bytes_read = process_sp->GetSTDERR (dst, dst_len, error); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetSTDERR (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, + process_sp.get(), + (int) bytes_read, + dst, + (uint64_t)dst_len, + (uint64_t)bytes_read); + + return bytes_read; +} + +size_t +SBProcess::GetAsyncProfileData(char *dst, size_t dst_len) const +{ + size_t bytes_read = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Error error; + bytes_read = process_sp->GetAsyncProfileData (dst, dst_len, error); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetProfileData (dst=\"%.*s\", dst_len=%" PRIu64 ") => %" PRIu64, + process_sp.get(), + (int) bytes_read, + dst, + (uint64_t)dst_len, + (uint64_t)bytes_read); + + return bytes_read; +} + +void +SBProcess::ReportEventState (const SBEvent &event, FILE *out) const +{ + if (out == NULL) + 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) +{ + 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) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + return process_sp->GetThreadList().SetSelectedThreadByID (thread.GetThreadID()); + } + return false; +} + +bool +SBProcess::SetSelectedThreadByID (lldb::tid_t tid) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = false; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ret_val = process_sp->GetThreadList().SetSelectedThreadByID (tid); + } + + if (log) + log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%4.4" PRIx64 ") => %s", + process_sp.get(), tid, (ret_val ? "true" : "false")); + + return ret_val; +} + +bool +SBProcess::SetSelectedThreadByIndexID (uint32_t index_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool ret_val = false; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ret_val = process_sp->GetThreadList().SetSelectedThreadByIndexID (index_id); + } + + if (log) + log->Printf ("SBProcess(%p)::SetSelectedThreadByID (tid=0x%x) => %s", + process_sp.get(), index_id, (ret_val ? "true" : "false")); + + return ret_val; +} + +SBThread +SBProcess::GetThreadAtIndex (size_t index) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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()); + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + thread_sp = process_sp->GetThreadList().GetThreadAtIndex(index, can_update); + sb_thread.SetThread (thread_sp); + } + + if (log) + { + log->Printf ("SBProcess(%p)::GetThreadAtIndex (index=%d) => SBThread(%p)", + process_sp.get(), (uint32_t) index, thread_sp.get()); + } + + return sb_thread; +} + +uint32_t +SBProcess::GetStopID(bool include_expression_stops) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + if (include_expression_stops) + return process_sp->GetStopID(); + else + return process_sp->GetLastNaturalStopID(); + } + return 0; +} + +StateType +SBProcess::GetState () +{ + + StateType ret_val = eStateInvalid; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ret_val = process_sp->GetState(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetState () => %s", + process_sp.get(), + lldb_private::StateAsCString (ret_val)); + + return ret_val; +} + + +int +SBProcess::GetExitStatus () +{ + int exit_status = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + exit_status = process_sp->GetExitStatus (); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetExitStatus () => %i (0x%8.8x)", + process_sp.get(), exit_status, exit_status); + + return exit_status; +} + +const char * +SBProcess::GetExitDescription () +{ + const char *exit_desc = NULL; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + exit_desc = process_sp->GetExitDescription (); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetExitDescription () => %s", + process_sp.get(), exit_desc); + return exit_desc; +} + +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(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetProcessID () => %" PRIu64, process_sp.get(), ret_val); + + return ret_val; +} + +uint32_t +SBProcess::GetUniqueID() +{ + uint32_t ret_val = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + ret_val = process_sp->GetUniqueID(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetUniqueID () => %" PRIu32, process_sp.get(), ret_val); + return ret_val; +} + +ByteOrder +SBProcess::GetByteOrder () const +{ + ByteOrder byteOrder = eByteOrderInvalid; + ProcessSP process_sp(GetSP()); + if (process_sp) + byteOrder = process_sp->GetTarget().GetArchitecture().GetByteOrder(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetByteOrder () => %d", process_sp.get(), byteOrder); + + return byteOrder; +} + +uint32_t +SBProcess::GetAddressByteSize () const +{ + uint32_t size = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + size = process_sp->GetTarget().GetArchitecture().GetAddressByteSize(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::GetAddressByteSize () => %d", process_sp.get(), size); + + return size; +} + +SBError +SBProcess::Continue () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBError sb_error; + ProcessSP process_sp(GetSP()); + + if (log) + log->Printf ("SBProcess(%p)::Continue ()...", process_sp.get()); + + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + + Error error (process_sp->Resume()); + if (error.Success()) + { + if (process_sp->GetTarget().GetDebugger().GetAsyncExecution () == false) + { + if (log) + log->Printf ("SBProcess(%p)::Continue () waiting for process to stop...", process_sp.get()); + process_sp->WaitForProcessToStop (NULL); + } + } + sb_error.SetError(error); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Continue () => SBError (%p): %s", process_sp.get(), sb_error.get(), sstr.GetData()); + } + + return sb_error; +} + + +SBError +SBProcess::Destroy () +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError(process_sp->Destroy()); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Destroy () => SBError (%p): %s", + process_sp.get(), + sb_error.get(), + sstr.GetData()); + } + + return sb_error; +} + + +SBError +SBProcess::Stop () +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Halt()); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Stop () => SBError (%p): %s", + process_sp.get(), + sb_error.get(), + sstr.GetData()); + } + + return sb_error; +} + +SBError +SBProcess::Kill () +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Destroy()); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Kill () => SBError (%p): %s", + process_sp.get(), + sb_error.get(), + sstr.GetData()); + } + + return sb_error; +} + +SBError +SBProcess::Detach () +{ + // FIXME: This should come from a process default. + bool keep_stopped = false; + return Detach (keep_stopped); +} + +SBError +SBProcess::Detach (bool keep_stopped) +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Detach(keep_stopped)); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + + return sb_error; +} + +SBError +SBProcess::Signal (int signo) +{ + SBError sb_error; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->Signal (signo)); + } + else + sb_error.SetErrorString ("SBProcess is invalid"); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::Signal (signo=%i) => SBError (%p): %s", + process_sp.get(), + signo, + sb_error.get(), + sstr.GetData()); + } + return sb_error; +} + +void +SBProcess::SendAsyncInterrupt () +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + process_sp->SendAsyncInterrupt (); + } +} + +SBThread +SBProcess::GetThreadByID (tid_t tid) +{ + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + Process::StopLocker stop_locker; + const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); + thread_sp = process_sp->GetThreadList().FindThreadByID (tid, can_update); + sb_thread.SetThread (thread_sp); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%4.4" PRIx64 ") => SBThread (%p)", + process_sp.get(), + tid, + thread_sp.get()); + } + + return sb_thread; +} + +SBThread +SBProcess::GetThreadByIndexID (uint32_t index_id) +{ + SBThread sb_thread; + ThreadSP thread_sp; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + Process::StopLocker stop_locker; + const bool can_update = stop_locker.TryLock(&process_sp->GetRunLock()); + thread_sp = process_sp->GetThreadList().FindThreadByIndexID (index_id, can_update); + sb_thread.SetThread (thread_sp); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBProcess(%p)::GetThreadByID (tid=0x%x) => SBThread (%p)", + process_sp.get(), + index_id, + thread_sp.get()); + } + + return sb_thread; +} + +StateType +SBProcess::GetStateFromEvent (const SBEvent &event) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + StateType ret_val = Process::ProcessEventData::GetStateFromEvent (event.get()); + + if (log) + log->Printf ("SBProcess::GetStateFromEvent (event.sp=%p) => %s", event.get(), + lldb_private::StateAsCString (ret_val)); + + return ret_val; +} + +bool +SBProcess::GetRestartedFromEvent (const SBEvent &event) +{ + return Process::ProcessEventData::GetRestartedFromEvent (event.get()); +} + +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) +{ + return Process::ProcessEventData::GetRestartedReasonAtIndex(event.get(), idx); +} + +SBProcess +SBProcess::GetProcessFromEvent (const SBEvent &event) +{ + SBProcess process(Process::ProcessEventData::GetProcessFromEvent (event.get())); + return process; +} + +bool +SBProcess::EventIsProcessEvent (const SBEvent &event) +{ + return strcmp (event.GetBroadcasterClass(), SBProcess::GetBroadcasterClass()) == 0; +} + +SBBroadcaster +SBProcess::GetBroadcaster () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ProcessSP process_sp(GetSP()); + + SBBroadcaster broadcaster(process_sp.get(), false); + + if (log) + log->Printf ("SBProcess(%p)::GetBroadcaster () => SBBroadcaster (%p)", process_sp.get(), + broadcaster.get()); + + return broadcaster; +} + +const char * +SBProcess::GetBroadcasterClass () +{ + return Process::GetStaticBroadcasterClass().AsCString(); +} + +size_t +SBProcess::ReadMemory (addr_t addr, void *dst, size_t dst_len, SBError &sb_error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + size_t bytes_read = 0; + + ProcessSP process_sp(GetSP()); + + if (log) + { + log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p))...", + process_sp.get(), + addr, + dst, + (uint64_t)dst_len, + sb_error.get()); + } + + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + bytes_read = process_sp->ReadMemory (addr, dst, dst_len, sb_error.ref()); + } + else + { + if (log) + log->Printf ("SBProcess(%p)::ReadMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::ReadMemory (addr=0x%" PRIx64 ", dst=%p, dst_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, + process_sp.get(), + addr, + dst, + (uint64_t)dst_len, + sb_error.get(), + sstr.GetData(), + (uint64_t)bytes_read); + } + + return bytes_read; +} + +size_t +SBProcess::ReadCStringFromMemory (addr_t addr, void *buf, size_t size, lldb::SBError &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())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + bytes_read = process_sp->ReadCStringFromMemory (addr, (char *)buf, size, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::ReadCStringFromMemory() => error: process is running", process_sp.get()); + 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) +{ + uint64_t value = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + value = process_sp->ReadUnsignedIntegerFromMemory (addr, byte_size, 0, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::ReadUnsignedFromMemory() => error: process is running", process_sp.get()); + 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::addr_t ptr = LLDB_INVALID_ADDRESS; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + ptr = process_sp->ReadPointerFromMemory (addr, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::ReadPointerFromMemory() => error: process is running", process_sp.get()); + 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) +{ + size_t bytes_written = 0; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + ProcessSP process_sp(GetSP()); + + if (log) + { + log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p))...", + process_sp.get(), + addr, + src, + (uint64_t)src_len, + sb_error.get()); + } + + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + bytes_written = process_sp->WriteMemory (addr, src, src_len, sb_error.ref()); + } + else + { + if (log) + log->Printf ("SBProcess(%p)::WriteMemory() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + + if (log) + { + SBStream sstr; + sb_error.GetDescription (sstr); + log->Printf ("SBProcess(%p)::WriteMemory (addr=0x%" PRIx64 ", src=%p, src_len=%" PRIu64 ", SBError (%p): %s) => %" PRIu64, + process_sp.get(), + addr, + src, + (uint64_t)src_len, + sb_error.get(), + sstr.GetData(), + (uint64_t)bytes_written); + } + + return bytes_written; +} + +bool +SBProcess::GetDescription (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 = NULL; + 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 +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num = 0; + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError(process_sp->GetWatchpointSupportInfo (num)); + if (log) + log->Printf ("SBProcess(%p)::GetNumSupportedHardwareWatchpoints () => %u", + process_sp.get(), num); + } + else + { + sb_error.SetErrorString ("SBProcess is invalid"); + } + return num; +} + +uint32_t +SBProcess::LoadImage (lldb::SBFileSpec &sb_image_spec, lldb::SBError &sb_error) +{ + ProcessSP process_sp(GetSP()); + if (process_sp) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&process_sp->GetRunLock())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + return process_sp->LoadImage (*sb_image_spec, sb_error.ref()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::LoadImage() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + return LLDB_INVALID_IMAGE_TOKEN; +} + +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())) + { + Mutex::Locker api_locker (process_sp->GetTarget().GetAPIMutex()); + sb_error.SetError (process_sp->UnloadImage (image_token)); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBProcess(%p)::UnloadImage() => error: process is running", process_sp.get()); + sb_error.SetErrorString("process is running"); + } + } + else + sb_error.SetErrorString("invalid process"); + return sb_error; +} diff --git a/source/API/SBSection.cpp b/source/API/SBSection.cpp new file mode 100644 index 00000000000..3fb84e81465 --- /dev/null +++ b/source/API/SBSection.cpp @@ -0,0 +1,291 @@ +//===-- SBSection.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSection.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ObjectFile.h" + + +using namespace lldb; +using namespace lldb_private; + + +SBSection::SBSection () : + m_opaque_wp () +{ +} + +SBSection::SBSection (const SBSection &rhs) : + m_opaque_wp (rhs.m_opaque_wp) +{ +} + + + +SBSection::SBSection (const lldb::SectionSP §ion_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) +{ + m_opaque_wp = rhs.m_opaque_wp; + return *this; +} + +SBSection::~SBSection () +{ +} + +bool +SBSection::IsValid () const +{ + SectionSP section_sp (GetSP()); + return section_sp && section_sp->GetModule().get() != NULL; +} + +const char * +SBSection::GetName () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetName().GetCString(); + return NULL; +} + +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 sb_section; +} + + +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 sb_section; +} + +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::SBSection sb_section; + SectionSP section_sp (GetSP()); + if (section_sp) + sb_section.SetSP (section_sp->GetChildren ().GetSectionAtIndex(idx)); + return sb_section; +} + +lldb::SectionSP +SBSection::GetSP() const +{ + return m_opaque_wp.lock(); +} + +void +SBSection::SetSP(const lldb::SectionSP §ion_sp) +{ + m_opaque_wp = section_sp; +} + +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) +{ + 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 () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetByteSize(); + return 0; +} + +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 () +{ + SectionSP section_sp (GetSP()); + if (section_sp) + return section_sp->GetFileSize(); + return 0; +} + +SBData +SBSection::GetSectionData () +{ + return GetSectionData (0, UINT64_MAX); +} + +SBData +SBSection::GetSectionData (uint64_t offset, uint64_t 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; + } + DataBufferSP data_buffer_sp (objfile->GetFileSpec().ReadFileContents (file_offset, file_size)); + 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 sb_data; +} + +SectionType +SBSection::GetSectionType () +{ + SectionSP section_sp (GetSP()); + if (section_sp.get()) + return section_sp->GetType(); + return eSectionTypeInvalid; +} + + +bool +SBSection::operator == (const 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) +{ + SectionSP lhs_section_sp (GetSP()); + SectionSP rhs_section_sp (rhs.GetSP()); + return lhs_section_sp != rhs_section_sp; +} + +bool +SBSection::GetDescription (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; +} + diff --git a/source/API/SBSourceManager.cpp b/source/API/SBSourceManager.cpp new file mode 100644 index 00000000000..0b8cbfceda0 --- /dev/null +++ b/source/API/SBSourceManager.cpp @@ -0,0 +1,146 @@ +//===-- SBSourceManager.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBSourceManager.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBStream.h" + +#include "lldb/API/SBFileSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/SourceManager.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 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, + 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, + 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) +{ + m_opaque_ap.reset(new SourceManagerImpl (debugger.get_sp())); +} + +SBSourceManager::SBSourceManager (const SBTarget &target) +{ + m_opaque_ap.reset(new SourceManagerImpl (target.GetSP())); +} + +SBSourceManager::SBSourceManager (const SBSourceManager &rhs) +{ + if (&rhs == this) + return; + + m_opaque_ap.reset(new SourceManagerImpl (*(rhs.m_opaque_ap.get()))); +} + +const lldb::SBSourceManager & +SBSourceManager::operator = (const lldb::SBSourceManager &rhs) +{ + m_opaque_ap.reset (new SourceManagerImpl (*(rhs.m_opaque_ap.get()))); + return *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 +) +{ + if (m_opaque_ap.get() == NULL) + return 0; + + return m_opaque_ap->DisplaySourceLinesWithLineNumbers (file.ref(), + line, + context_before, + context_after, + current_line_cstr, + s.get()); +} diff --git a/source/API/SBStream.cpp b/source/API/SBStream.cpp new file mode 100644 index 00000000000..dc8eb05ab0b --- /dev/null +++ b/source/API/SBStream.cpp @@ -0,0 +1,187 @@ +//===-- SBStream.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBStream.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" + +using namespace lldb; +using namespace lldb_private; + +SBStream::SBStream () : + m_opaque_ap (new StreamString()), + m_is_file (false) +{ +} + +SBStream::~SBStream () +{ +} + +bool +SBStream::IsValid() const +{ + return (m_opaque_ap.get() != NULL); +} + +// 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 () +{ + if (m_is_file || m_opaque_ap.get() == NULL) + return NULL; + + return static_cast(m_opaque_ap.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() +{ + if (m_is_file || m_opaque_ap.get() == NULL) + return 0; + + return static_cast(m_opaque_ap.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) +{ + std::string local_data; + if (m_opaque_ap.get()) + { + // 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.swap(static_cast(m_opaque_ap.get())->GetString()); + } + StreamFile *stream_file = new StreamFile; + uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; + if (append) + open_options |= File::eOpenOptionAppend; + stream_file->GetFile().Open (path, open_options, File::ePermissionsDefault); + + m_opaque_ap.reset (stream_file); + + if (m_opaque_ap.get()) + { + 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_ap->Write (&local_data[0], local_data.size()); + } + else + m_is_file = false; +} + +void +SBStream::RedirectToFileHandle (FILE *fh, bool transfer_fh_ownership) +{ + std::string local_data; + if (m_opaque_ap.get()) + { + // 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.swap(static_cast(m_opaque_ap.get())->GetString()); + } + m_opaque_ap.reset (new StreamFile (fh, transfer_fh_ownership)); + + if (m_opaque_ap.get()) + { + 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_ap->Write (&local_data[0], local_data.size()); + } + else + m_is_file = false; +} + +void +SBStream::RedirectToFileDescriptor (int fd, bool transfer_fh_ownership) +{ + std::string local_data; + if (m_opaque_ap.get()) + { + // 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.swap(static_cast(m_opaque_ap.get())->GetString()); + } + + m_opaque_ap.reset (new StreamFile (::fdopen (fd, "w"), transfer_fh_ownership)); + if (m_opaque_ap.get()) + { + 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_ap->Write (&local_data[0], local_data.size()); + } + else + m_is_file = false; + +} + +lldb_private::Stream * +SBStream::operator->() +{ + return m_opaque_ap.get(); +} + +lldb_private::Stream * +SBStream::get() +{ + return m_opaque_ap.get(); +} + +lldb_private::Stream & +SBStream::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new StreamString()); + return *m_opaque_ap.get(); +} + +void +SBStream::Clear () +{ + if (m_opaque_ap.get()) + { + // 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_ap.reset(); + else + static_cast(m_opaque_ap.get())->GetString().clear(); + } +} diff --git a/source/API/SBStringList.cpp b/source/API/SBStringList.cpp new file mode 100644 index 00000000000..129d2f4c11f --- /dev/null +++ b/source/API/SBStringList.cpp @@ -0,0 +1,136 @@ +//===-- SBStringList.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBStringList.h" + +#include "lldb/Core/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +SBStringList::SBStringList () : + m_opaque_ap () +{ +} + +SBStringList::SBStringList (const lldb_private::StringList *lldb_strings_ptr) : + m_opaque_ap () +{ + if (lldb_strings_ptr) + m_opaque_ap.reset (new lldb_private::StringList (*lldb_strings_ptr)); +} + +SBStringList::SBStringList (const SBStringList &rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + m_opaque_ap.reset (new lldb_private::StringList(*rhs)); +} + + +const SBStringList & +SBStringList::operator = (const SBStringList &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset(new lldb_private::StringList(*rhs)); + else + m_opaque_ap.reset(); + } + return *this; +} + +SBStringList::~SBStringList () +{ +} + +const lldb_private::StringList * +SBStringList::operator->() const +{ + return m_opaque_ap.get(); +} + +const lldb_private::StringList & +SBStringList::operator*() const +{ + return *m_opaque_ap; +} + +bool +SBStringList::IsValid() const +{ + return (m_opaque_ap.get() != NULL); +} + +void +SBStringList::AppendString (const char *str) +{ + if (str != NULL) + { + if (IsValid()) + m_opaque_ap->AppendString (str); + else + m_opaque_ap.reset (new lldb_private::StringList (str)); + } + +} + +void +SBStringList::AppendList (const char **strv, int strc) +{ + if ((strv != NULL) + && (strc > 0)) + { + if (IsValid()) + m_opaque_ap->AppendList (strv, strc); + else + m_opaque_ap.reset (new lldb_private::StringList (strv, strc)); + } +} + +void +SBStringList::AppendList (const SBStringList &strings) +{ + if (strings.IsValid()) + { + if (! IsValid()) + m_opaque_ap.reset (new lldb_private::StringList()); + m_opaque_ap->AppendList (*(strings.m_opaque_ap)); + } +} + +uint32_t +SBStringList::GetSize () const +{ + if (IsValid()) + { + return m_opaque_ap->GetSize(); + } + return 0; +} + +const char * +SBStringList::GetStringAtIndex (size_t idx) +{ + if (IsValid()) + { + return m_opaque_ap->GetStringAtIndex (idx); + } + return NULL; +} + +void +SBStringList::Clear () +{ + if (IsValid()) + { + m_opaque_ap->Clear(); + } +} diff --git a/source/API/SBSymbol.cpp b/source/API/SBSymbol.cpp new file mode 100644 index 00000000000..dd057e81a55 --- /dev/null +++ b/source/API/SBSymbol.cpp @@ -0,0 +1,223 @@ +//===-- SBSymbol.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSymbol.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.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 (NULL) +{ +} + +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) +{ +} + +const SBSymbol & +SBSymbol::operator = (const SBSymbol &rhs) +{ + m_opaque_ptr = rhs.m_opaque_ptr; + return *this; +} + +SBSymbol::~SBSymbol () +{ + m_opaque_ptr = NULL; +} + +void +SBSymbol::SetSymbol (lldb_private::Symbol *lldb_object_ptr) +{ + m_opaque_ptr = lldb_object_ptr; +} + +bool +SBSymbol::IsValid () const +{ + return m_opaque_ptr != NULL; +} + +const char * +SBSymbol::GetName() const +{ + const char *name = NULL; + if (m_opaque_ptr) + name = m_opaque_ptr->GetMangled().GetName().AsCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBSymbol(%p)::GetName () => \"%s\"", m_opaque_ptr, name ? name : ""); + return name; +} + +const char * +SBSymbol::GetMangledName () const +{ + const char *name = NULL; + if (m_opaque_ptr) + name = m_opaque_ptr->GetMangled().GetMangledName().AsCString(); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBSymbol(%p)::GetMangledName () => \"%s\"", m_opaque_ptr, name ? name : ""); + + return name; +} + + +bool +SBSymbol::operator == (const SBSymbol &rhs) const +{ + return m_opaque_ptr == rhs.m_opaque_ptr; +} + +bool +SBSymbol::operator != (const SBSymbol &rhs) const +{ + return m_opaque_ptr != rhs.m_opaque_ptr; +} + +bool +SBSymbol::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ptr) + { + m_opaque_ptr->GetDescription (&strm, + lldb::eDescriptionLevelFull, NULL); + } + else + strm.PutCString ("No value"); + + return true; +} + +SBInstructionList +SBSymbol::GetInstructions (SBTarget target) +{ + return GetInstructions (target, NULL); +} + +SBInstructionList +SBSymbol::GetInstructions (SBTarget target, const char *flavor_string) +{ + SBInstructionList sb_instructions; + if (m_opaque_ptr) + { + Mutex::Locker api_locker; + ExecutionContext exe_ctx; + TargetSP target_sp (target.GetSP()); + if (target_sp) + { + api_locker.Lock (target_sp->GetAPIMutex()); + target_sp->CalculateExecutionContext (exe_ctx); + } + if (m_opaque_ptr->ValueIsAddress()) + { + ModuleSP module_sp (m_opaque_ptr->GetAddress().GetModule()); + if (module_sp) + { + AddressRange symbol_range (m_opaque_ptr->GetAddress(), m_opaque_ptr->GetByteSize()); + sb_instructions.SetDisassembler (Disassembler::DisassembleRange (module_sp->GetArchitecture (), + NULL, + flavor_string, + exe_ctx, + symbol_range)); + } + } + } + return 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 () +{ + SBAddress addr; + if (m_opaque_ptr && m_opaque_ptr->ValueIsAddress()) + { + addr.SetAddress (&m_opaque_ptr->GetAddress()); + } + return addr; +} + +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->GetAddress()); + addr->Slide (m_opaque_ptr->GetByteSize()); + } + } + return addr; +} + +uint32_t +SBSymbol::GetPrologueByteSize () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetPrologueByteSize(); + return 0; +} + +SymbolType +SBSymbol::GetType () +{ + if (m_opaque_ptr) + return m_opaque_ptr->GetType(); + return eSymbolTypeInvalid; +} + +bool +SBSymbol::IsExternal() +{ + if (m_opaque_ptr) + return m_opaque_ptr->IsExternal(); + return false; +} + +bool +SBSymbol::IsSynthetic() +{ + if (m_opaque_ptr) + return m_opaque_ptr->IsSynthetic(); + return false; +} + diff --git a/source/API/SBSymbolContext.cpp b/source/API/SBSymbolContext.cpp new file mode 100644 index 00000000000..479b0f75bfe --- /dev/null +++ b/source/API/SBSymbolContext.cpp @@ -0,0 +1,285 @@ +//===-- SBSymbolContext.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.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_ap () +{ +} + +SBSymbolContext::SBSymbolContext (const SymbolContext *sc_ptr) : + m_opaque_ap () +{ + if (sc_ptr) + m_opaque_ap.reset (new SymbolContext (*sc_ptr)); +} + +SBSymbolContext::SBSymbolContext (const SBSymbolContext& rhs) : + m_opaque_ap () +{ + if (rhs.IsValid()) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *rhs.m_opaque_ap; + else + ref() = *rhs.m_opaque_ap; + } +} + +SBSymbolContext::~SBSymbolContext () +{ +} + +const SBSymbolContext & +SBSymbolContext::operator = (const SBSymbolContext &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset (new lldb_private::SymbolContext(*rhs.m_opaque_ap.get())); + } + return *this; +} + +void +SBSymbolContext::SetSymbolContext (const SymbolContext *sc_ptr) +{ + if (sc_ptr) + { + if (m_opaque_ap.get()) + *m_opaque_ap = *sc_ptr; + else + m_opaque_ap.reset (new SymbolContext (*sc_ptr)); + } + else + { + if (m_opaque_ap.get()) + m_opaque_ap->Clear(true); + } +} + +bool +SBSymbolContext::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + + + +SBModule +SBSymbolContext::GetModule () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBModule sb_module; + ModuleSP module_sp; + if (m_opaque_ap.get()) + { + module_sp = m_opaque_ap->module_sp; + sb_module.SetSP (module_sp); + } + + if (log) + { + SBStream sstr; + sb_module.GetDescription (sstr); + log->Printf ("SBSymbolContext(%p)::GetModule () => SBModule(%p): %s", + m_opaque_ap.get(), module_sp.get(), sstr.GetData()); + } + + return sb_module; +} + +SBCompileUnit +SBSymbolContext::GetCompileUnit () +{ + return SBCompileUnit (m_opaque_ap.get() ? m_opaque_ap->comp_unit : NULL); +} + +SBFunction +SBSymbolContext::GetFunction () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Function *function = NULL; + + if (m_opaque_ap.get()) + function = m_opaque_ap->function; + + SBFunction sb_function (function); + + if (log) + log->Printf ("SBSymbolContext(%p)::GetFunction () => SBFunction(%p)", + m_opaque_ap.get(), function); + + return sb_function; +} + +SBBlock +SBSymbolContext::GetBlock () +{ + return SBBlock (m_opaque_ap.get() ? m_opaque_ap->block : NULL); +} + +SBLineEntry +SBSymbolContext::GetLineEntry () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBLineEntry sb_line_entry; + if (m_opaque_ap.get()) + sb_line_entry.SetLineEntry (m_opaque_ap->line_entry); + + if (log) + { + log->Printf ("SBSymbolContext(%p)::GetLineEntry () => SBLineEntry(%p)", + m_opaque_ap.get(), sb_line_entry.get()); + } + + return sb_line_entry; +} + +SBSymbol +SBSymbolContext::GetSymbol () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Symbol *symbol = NULL; + + if (m_opaque_ap.get()) + symbol = m_opaque_ap->symbol; + + SBSymbol sb_symbol (symbol); + + if (log) + { + log->Printf ("SBSymbolContext(%p)::GetSymbol () => SBSymbol(%p)", + m_opaque_ap.get(), symbol); + } + + return sb_symbol; +} + +void +SBSymbolContext::SetModule (lldb::SBModule module) +{ + ref().module_sp = module.GetSP(); +} + +void +SBSymbolContext::SetCompileUnit (lldb::SBCompileUnit compile_unit) +{ + ref().comp_unit = compile_unit.get(); +} + +void +SBSymbolContext::SetFunction (lldb::SBFunction function) +{ + ref().function = function.get(); +} + +void +SBSymbolContext::SetBlock (lldb::SBBlock block) +{ + ref().block = block.GetPtr(); +} + +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) +{ + ref().symbol = symbol.get(); +} + + +lldb_private::SymbolContext* +SBSymbolContext::operator->() const +{ + return m_opaque_ap.get(); +} + + +const lldb_private::SymbolContext& +SBSymbolContext::operator*() const +{ + assert (m_opaque_ap.get()); + return *m_opaque_ap.get(); +} + + +lldb_private::SymbolContext& +SBSymbolContext::operator*() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new SymbolContext); + return *m_opaque_ap.get(); +} + +lldb_private::SymbolContext& +SBSymbolContext::ref() +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new SymbolContext); + return *m_opaque_ap.get(); +} + +lldb_private::SymbolContext * +SBSymbolContext::get() const +{ + return m_opaque_ap.get(); +} + +bool +SBSymbolContext::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + m_opaque_ap->GetDescription (&strm, lldb::eDescriptionLevelFull, NULL); + } + else + strm.PutCString ("No value"); + + return true; +} + +SBSymbolContext +SBSymbolContext::GetParentOfInlinedScope (const SBAddress &curr_frame_pc, + SBAddress &parent_frame_addr) const +{ + SBSymbolContext sb_sc; + if (m_opaque_ap.get() && curr_frame_pc.IsValid()) + { + if (m_opaque_ap->GetParentOfInlinedScope (curr_frame_pc.ref(), sb_sc.ref(), parent_frame_addr.ref())) + return sb_sc; + } + return SBSymbolContext(); +} + diff --git a/source/API/SBSymbolContextList.cpp b/source/API/SBSymbolContextList.cpp new file mode 100644 index 00000000000..0730096c5f3 --- /dev/null +++ b/source/API/SBSymbolContextList.cpp @@ -0,0 +1,117 @@ +//===-- SBSymbolContextList.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBSymbolContextList.h" +#include "lldb/API/SBStream.h" +#include "lldb/Symbol/SymbolContext.h" + +using namespace lldb; +using namespace lldb_private; + +SBSymbolContextList::SBSymbolContextList () : + m_opaque_ap (new SymbolContextList()) +{ +} + +SBSymbolContextList::SBSymbolContextList (const SBSymbolContextList& rhs) : + m_opaque_ap (new SymbolContextList(*rhs.m_opaque_ap)) +{ +} + +SBSymbolContextList::~SBSymbolContextList () +{ +} + +const SBSymbolContextList & +SBSymbolContextList::operator = (const SBSymbolContextList &rhs) +{ + if (this != &rhs) + { + *m_opaque_ap = *rhs.m_opaque_ap; + } + return *this; +} + +uint32_t +SBSymbolContextList::GetSize() const +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetSize(); + return 0; +} + +SBSymbolContext +SBSymbolContextList::GetContextAtIndex (uint32_t idx) +{ + SBSymbolContext sb_sc; + if (m_opaque_ap.get()) + { + SymbolContext sc; + if (m_opaque_ap->GetContextAtIndex (idx, sc)) + { + sb_sc.SetSymbolContext(&sc); + } + } + return sb_sc; +} + +void +SBSymbolContextList::Clear() +{ + if (m_opaque_ap.get()) + m_opaque_ap->Clear(); +} + +void +SBSymbolContextList::Append(SBSymbolContext &sc) +{ + if (sc.IsValid() && m_opaque_ap.get()) + m_opaque_ap->Append(*sc); +} + +void +SBSymbolContextList::Append(SBSymbolContextList &sc_list) +{ + if (sc_list.IsValid() && m_opaque_ap.get()) + m_opaque_ap->Append(*sc_list); +} + + +bool +SBSymbolContextList::IsValid () const +{ + return m_opaque_ap.get() != NULL; +} + + + +lldb_private::SymbolContextList* +SBSymbolContextList::operator->() const +{ + return m_opaque_ap.get(); +} + + +lldb_private::SymbolContextList& +SBSymbolContextList::operator*() const +{ + assert (m_opaque_ap.get()); + return *m_opaque_ap.get(); +} + +bool +SBSymbolContextList::GetDescription (lldb::SBStream &description) +{ + Stream &strm = description.ref(); + if (m_opaque_ap.get()) + m_opaque_ap->GetDescription (&strm, lldb::eDescriptionLevelFull, NULL); + return true; +} + + diff --git a/source/API/SBTarget.cpp b/source/API/SBTarget.cpp new file mode 100644 index 00000000000..f37c8f8a614 --- /dev/null +++ b/source/API/SBTarget.cpp @@ -0,0 +1,2660 @@ +//===-- SBTarget.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTarget.h" + +#include "lldb/lldb-public.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBBreakpoint.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/SBSourceManager.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.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/ArchSpec.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/STLUtils.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/TargetList.h" + +#include "lldb/Interpreter/CommandReturnObject.h" +#include "../source/Commands/CommandObjectBreakpoint.h" + + +using namespace lldb; +using namespace lldb_private; + +#define DEFAULT_DISASM_BYTE_SIZE 32 + +SBLaunchInfo::SBLaunchInfo (const char **argv) : + m_opaque_sp(new ProcessLaunchInfo()) +{ + m_opaque_sp->GetFlags().Reset (eLaunchFlagDebug | eLaunchFlagDisableASLR); + if (argv && argv[0]) + m_opaque_sp->GetArguments().SetArguments(argv); +} + +SBLaunchInfo::~SBLaunchInfo() +{ +} + +lldb_private::ProcessLaunchInfo & +SBLaunchInfo::ref () +{ + return *m_opaque_sp; +} + + +uint32_t +SBLaunchInfo::GetUserID() +{ + return m_opaque_sp->GetUserID(); +} + +uint32_t +SBLaunchInfo::GetGroupID() +{ + return m_opaque_sp->GetGroupID(); +} + +bool +SBLaunchInfo::UserIDIsValid () +{ + return m_opaque_sp->UserIDIsValid(); +} + +bool +SBLaunchInfo::GroupIDIsValid () +{ + return m_opaque_sp->GroupIDIsValid(); +} + +void +SBLaunchInfo::SetUserID (uint32_t uid) +{ + m_opaque_sp->SetUserID (uid); +} + +void +SBLaunchInfo::SetGroupID (uint32_t gid) +{ + m_opaque_sp->SetGroupID (gid); +} + +uint32_t +SBLaunchInfo::GetNumArguments () +{ + return m_opaque_sp->GetArguments().GetArgumentCount(); +} + +const char * +SBLaunchInfo::GetArgumentAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetArguments().GetArgumentAtIndex(idx); +} + +void +SBLaunchInfo::SetArguments (const char **argv, bool 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 () +{ + return m_opaque_sp->GetEnvironmentEntries().GetArgumentCount(); +} + +const char * +SBLaunchInfo::GetEnvironmentEntryAtIndex (uint32_t idx) +{ + return m_opaque_sp->GetEnvironmentEntries().GetArgumentAtIndex(idx); +} + +void +SBLaunchInfo::SetEnvironmentEntries (const char **envp, bool append) +{ + if (append) + { + if (envp) + m_opaque_sp->GetEnvironmentEntries().AppendArguments(envp); + } + else + { + if (envp) + m_opaque_sp->GetEnvironmentEntries().SetArguments(envp); + else + m_opaque_sp->GetEnvironmentEntries().Clear(); + } +} + +void +SBLaunchInfo::Clear () +{ + m_opaque_sp->Clear(); +} + +const char * +SBLaunchInfo::GetWorkingDirectory () const +{ + return m_opaque_sp->GetWorkingDirectory(); +} + +void +SBLaunchInfo::SetWorkingDirectory (const char *working_dir) +{ + m_opaque_sp->SetWorkingDirectory(working_dir); +} + +uint32_t +SBLaunchInfo::GetLaunchFlags () +{ + return m_opaque_sp->GetFlags().Get(); +} + +void +SBLaunchInfo::SetLaunchFlags (uint32_t flags) +{ + m_opaque_sp->GetFlags().Reset(flags); +} + +const char * +SBLaunchInfo::GetProcessPluginName () +{ + return m_opaque_sp->GetProcessPluginName(); +} + +void +SBLaunchInfo::SetProcessPluginName (const char *plugin_name) +{ + return m_opaque_sp->SetProcessPluginName (plugin_name); +} + +const char * +SBLaunchInfo::GetShell () +{ + return m_opaque_sp->GetShell(); +} + +void +SBLaunchInfo::SetShell (const char * path) +{ + m_opaque_sp->SetShell (path); +} + +uint32_t +SBLaunchInfo::GetResumeCount () +{ + return m_opaque_sp->GetResumeCount(); +} + +void +SBLaunchInfo::SetResumeCount (uint32_t c) +{ + m_opaque_sp->SetResumeCount (c); +} + +bool +SBLaunchInfo::AddCloseFileAction (int fd) +{ + return m_opaque_sp->AppendCloseFileAction(fd); +} + +bool +SBLaunchInfo::AddDuplicateFileAction (int fd, int dup_fd) +{ + return m_opaque_sp->AppendDuplicateFileAction(fd, dup_fd); +} + +bool +SBLaunchInfo::AddOpenFileAction (int fd, const char *path, bool read, bool write) +{ + return m_opaque_sp->AppendOpenFileAction(fd, path, read, write); +} + +bool +SBLaunchInfo::AddSuppressFileAction (int fd, bool read, bool write) +{ + return m_opaque_sp->AppendSuppressFileAction(fd, read, write); +} + + +SBAttachInfo::SBAttachInfo () : + m_opaque_sp (new ProcessAttachInfo()) +{ +} + +SBAttachInfo::SBAttachInfo (lldb::pid_t pid) : + m_opaque_sp (new ProcessAttachInfo()) +{ + m_opaque_sp->SetProcessID (pid); +} + +SBAttachInfo::SBAttachInfo (const char *path, bool wait_for) : + m_opaque_sp (new ProcessAttachInfo()) +{ + if (path && path[0]) + m_opaque_sp->GetExecutableFile().SetFile(path, false); + m_opaque_sp->SetWaitForLaunch (wait_for); +} + +SBAttachInfo::SBAttachInfo (const SBAttachInfo &rhs) : + m_opaque_sp (new ProcessAttachInfo()) +{ + *m_opaque_sp = *rhs.m_opaque_sp; +} + +SBAttachInfo::~SBAttachInfo() +{ +} + +lldb_private::ProcessAttachInfo & +SBAttachInfo::ref () +{ + return *m_opaque_sp; +} + +SBAttachInfo & +SBAttachInfo::operator = (const SBAttachInfo &rhs) +{ + if (this != &rhs) + *m_opaque_sp = *rhs.m_opaque_sp; + return *this; +} + +lldb::pid_t +SBAttachInfo::GetProcessID () +{ + return m_opaque_sp->GetProcessID(); +} + +void +SBAttachInfo::SetProcessID (lldb::pid_t pid) +{ + m_opaque_sp->SetProcessID (pid); +} + + +uint32_t +SBAttachInfo::GetResumeCount () +{ + return m_opaque_sp->GetResumeCount(); +} + +void +SBAttachInfo::SetResumeCount (uint32_t c) +{ + m_opaque_sp->SetResumeCount (c); +} + +const char * +SBAttachInfo::GetProcessPluginName () +{ + return m_opaque_sp->GetProcessPluginName(); +} + +void +SBAttachInfo::SetProcessPluginName (const char *plugin_name) +{ + return m_opaque_sp->SetProcessPluginName (plugin_name); +} + +void +SBAttachInfo::SetExecutable (const char *path) +{ + if (path && path[0]) + m_opaque_sp->GetExecutableFile().SetFile(path, false); + else + m_opaque_sp->GetExecutableFile().Clear(); +} + +void +SBAttachInfo::SetExecutable (SBFileSpec exe_file) +{ + if (exe_file.IsValid()) + m_opaque_sp->GetExecutableFile() = exe_file.ref(); + else + m_opaque_sp->GetExecutableFile().Clear(); +} + +bool +SBAttachInfo::GetWaitForLaunch () +{ + return m_opaque_sp->GetWaitForLaunch(); +} + +void +SBAttachInfo::SetWaitForLaunch (bool b) +{ + m_opaque_sp->SetWaitForLaunch (b); +} + +bool +SBAttachInfo::GetIgnoreExisting () +{ + return m_opaque_sp->GetIgnoreExisting(); +} + +void +SBAttachInfo::SetIgnoreExisting (bool b) +{ + m_opaque_sp->SetIgnoreExisting (b); +} + +uint32_t +SBAttachInfo::GetUserID() +{ + return m_opaque_sp->GetUserID(); +} + +uint32_t +SBAttachInfo::GetGroupID() +{ + return m_opaque_sp->GetGroupID(); +} + +bool +SBAttachInfo::UserIDIsValid () +{ + return m_opaque_sp->UserIDIsValid(); +} + +bool +SBAttachInfo::GroupIDIsValid () +{ + return m_opaque_sp->GroupIDIsValid(); +} + +void +SBAttachInfo::SetUserID (uint32_t uid) +{ + m_opaque_sp->SetUserID (uid); +} + +void +SBAttachInfo::SetGroupID (uint32_t gid) +{ + m_opaque_sp->SetGroupID (gid); +} + +uint32_t +SBAttachInfo::GetEffectiveUserID() +{ + return m_opaque_sp->GetEffectiveUserID(); +} + +uint32_t +SBAttachInfo::GetEffectiveGroupID() +{ + return m_opaque_sp->GetEffectiveGroupID(); +} + +bool +SBAttachInfo::EffectiveUserIDIsValid () +{ + return m_opaque_sp->EffectiveUserIDIsValid(); +} + +bool +SBAttachInfo::EffectiveGroupIDIsValid () +{ + return m_opaque_sp->EffectiveGroupIDIsValid (); +} + +void +SBAttachInfo::SetEffectiveUserID (uint32_t uid) +{ + m_opaque_sp->SetEffectiveUserID(uid); +} + +void +SBAttachInfo::SetEffectiveGroupID (uint32_t gid) +{ + m_opaque_sp->SetEffectiveGroupID(gid); +} + +lldb::pid_t +SBAttachInfo::GetParentProcessID () +{ + return m_opaque_sp->GetParentProcessID(); +} + +void +SBAttachInfo::SetParentProcessID (lldb::pid_t pid) +{ + m_opaque_sp->SetParentProcessID (pid); +} + +bool +SBAttachInfo::ParentProcessIDIsValid() +{ + return m_opaque_sp->ParentProcessIDIsValid(); +} + + +//---------------------------------------------------------------------- +// SBTarget constructor +//---------------------------------------------------------------------- +SBTarget::SBTarget () : + m_opaque_sp () +{ +} + +SBTarget::SBTarget (const SBTarget& rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +SBTarget::SBTarget(const TargetSP& target_sp) : + m_opaque_sp (target_sp) +{ +} + +const SBTarget& +SBTarget::operator = (const SBTarget& rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBTarget::~SBTarget() +{ +} + +const char * +SBTarget::GetBroadcasterClassName () +{ + return Target::GetStaticBroadcasterClass().AsCString(); +} + +bool +SBTarget::IsValid () const +{ + return m_opaque_sp.get() != NULL && m_opaque_sp->IsValid(); +} + +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); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBTarget(%p)::GetProcess () => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + +SBDebugger +SBTarget::GetDebugger () const +{ + SBDebugger debugger; + TargetSP target_sp(GetSP()); + if (target_sp) + debugger.reset (target_sp->GetDebugger().shared_from_this()); + return debugger; +} + +SBProcess +SBTarget::LoadCore (const char *core_file) +{ + SBProcess sb_process; + TargetSP target_sp(GetSP()); + if (target_sp) + { + FileSpec filespec(core_file, true); + ProcessSP process_sp (target_sp->CreateProcess(target_sp->GetDebugger().GetListener(), + NULL, + &filespec)); + if (process_sp) + { + process_sp->LoadCore(); + sb_process.SetSP (process_sp); + } + } + return sb_process; +} + +SBProcess +SBTarget::LaunchSimple +( + char const **argv, + char const **envp, + const char *working_directory +) +{ + char *stdin_path = NULL; + char *stdout_path = NULL; + char *stderr_path = NULL; + uint32_t launch_flags = 0; + bool stop_at_entry = false; + SBError error; + SBListener listener = GetDebugger().GetListener(); + return Launch (listener, + argv, + envp, + stdin_path, + stdout_path, + stderr_path, + working_directory, + launch_flags, + stop_at_entry, + 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 +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::Launch (argv=%p, envp=%p, stdin=%s, stdout=%s, stderr=%s, working-dir=%s, launch_flags=0x%x, stop_at_entry=%i, &error (%p))...", + target_sp.get(), + argv, + envp, + stdin_path ? stdin_path : "NULL", + stdout_path ? stdout_path : "NULL", + stderr_path ? stderr_path : "NULL", + working_directory ? working_directory : "NULL", + launch_flags, + stop_at_entry, + error.get()); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + 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 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 sb_process; + } + } + else + { + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + } + + if (process_sp) + { + sb_process.SetSP (process_sp); + if (getenv("LLDB_LAUNCH_FLAG_DISABLE_STDIO")) + launch_flags |= eLaunchFlagDisableSTDIO; + + ProcessLaunchInfo launch_info (stdin_path, stdout_path, stderr_path, 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.GetEnvironmentEntries ().SetArguments (envp); + + error.SetError (process_sp->Launch (launch_info)); + if (error.Success()) + { + // We we are stopping at the entry point, we can return now! + if (stop_at_entry) + return sb_process; + + // Make sure we are stopped at the entry + StateType state = process_sp->WaitForProcessToStop (NULL); + if (state == eStateStopped) + { + // resume the process to skip the entry point + error.SetError (process_sp->Resume()); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop yet again! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + +SBProcess +SBTarget::Launch (SBLaunchInfo &sb_launch_info, SBError& error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::Launch (launch_info, error)...", target_sp.get()); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + 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 sb_process; + } + } + + if (state != eStateConnected) + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + + if (process_sp) + { + sb_process.SetSP (process_sp); + lldb_private::ProcessLaunchInfo &launch_info = sb_launch_info.ref(); + + 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 (process_sp->Launch (launch_info)); + const bool synchronous_execution = target_sp->GetDebugger().GetAsyncExecution () == false; + if (error.Success()) + { + if (launch_info.GetFlags().Test(eLaunchFlagStopAtEntry)) + { + // If we are doing synchronous mode, then wait for the initial + // stop to happen, else, return and let the caller watch for + // the stop + if (synchronous_execution) + process_sp->WaitForProcessToStop (NULL); + // We we are stopping at the entry point, we can return now! + return sb_process; + } + + // Make sure we are stopped at the entry + StateType state = process_sp->WaitForProcessToStop (NULL); + if (state == eStateStopped) + { + // resume the process to skip the entry point + error.SetError (process_sp->Resume()); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop yet again! + if (synchronous_execution) + process_sp->WaitForProcessToStop (NULL); + } + } + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API); + if (log) + { + log->Printf ("SBTarget(%p)::Launch (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + +lldb::SBProcess +SBTarget::Attach (SBAttachInfo &sb_attach_info, SBError& error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::Attach (sb_attach_info, error)...", target_sp.get()); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + 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"); + if (log) + { + log->Printf ("SBTarget(%p)::Attach (...) => error %s", + target_sp.get(), error.GetCString()); + } + return sb_process; + } + } + + if (state != eStateConnected) + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + + if (process_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); + if (log) + { + log->Printf ("SBTarget(%p)::Attach (...) => error %s", + target_sp.get(), error.GetCString()); + } + return sb_process; + } + } + } + error.SetError (process_sp->Attach (attach_info)); + if (error.Success()) + { + sb_process.SetSP (process_sp); + // If we are doing synchronous mode, then wait for the + // process to stop! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::Attach (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + + return sb_process; +} + + +#if defined(__APPLE__) + +lldb::SBProcess +SBTarget::AttachToProcessWithID (SBListener &listener, + ::pid_t pid, + lldb::SBError& error) +{ + return AttachToProcessWithID (listener, (lldb::pid_t)pid, error); +} + +#endif // #if defined(__APPLE__) + +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 +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToProcessWithID (listener, pid=%" PRId64 ", error)...", target_sp.get(), pid); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + 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 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 sb_process; + } + } + else + { + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + } + if (process_sp) + { + sb_process.SetSP (process_sp); + + ProcessAttachInfo attach_info; + attach_info.SetProcessID (pid); + + PlatformSP platform_sp = target_sp->GetPlatform(); + ProcessInstanceInfo instance_info; + if (platform_sp->GetProcessInfo(pid, instance_info)) + { + attach_info.SetUserID(instance_info.GetEffectiveUserID()); + } + error.SetError (process_sp->Attach (attach_info)); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToProcessWithID (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + return 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 +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToProcessWithName (listener, name=%s, wait_for=%s, error)...", target_sp.get(), name, wait_for ? "true" : "false"); + } + + if (name && target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + 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 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 sb_process; + } + } + else + { + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), NULL, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), NULL, NULL); + } + + if (process_sp) + { + sb_process.SetSP (process_sp); + ProcessAttachInfo attach_info; + attach_info.GetExecutableFile().SetFile(name, false); + attach_info.SetWaitForLaunch(wait_for); + error.SetError (process_sp->Attach (attach_info)); + if (error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop! + if (target_sp->GetDebugger().GetAsyncExecution () == false) + process_sp->WaitForProcessToStop (NULL); + } + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::AttachToPorcessWithName (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + return sb_process; +} + +lldb::SBProcess +SBTarget::ConnectRemote +( + SBListener &listener, + const char *url, + const char *plugin_name, + SBError& error +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBProcess sb_process; + ProcessSP process_sp; + TargetSP target_sp(GetSP()); + + if (log) + { + log->Printf ("SBTarget(%p)::ConnectRemote (listener, url=%s, plugin_name=%s, error)...", target_sp.get(), url, plugin_name); + } + + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (listener.IsValid()) + process_sp = target_sp->CreateProcess (listener.ref(), plugin_name, NULL); + else + process_sp = target_sp->CreateProcess (target_sp->GetDebugger().GetListener(), plugin_name, NULL); + + + if (process_sp) + { + sb_process.SetSP (process_sp); + error.SetError (process_sp->ConnectRemote (NULL, url)); + } + else + { + error.SetErrorString ("unable to create lldb_private::Process"); + } + } + else + { + error.SetErrorString ("SBTarget is invalid"); + } + + if (log) + { + log->Printf ("SBTarget(%p)::ConnectRemote (...) => SBProcess(%p)", + target_sp.get(), process_sp.get()); + } + return sb_process; +} + +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()); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + log->Printf ("SBTarget(%p)::GetExecutable () => SBFileSpec(%p)", + target_sp.get(), exe_file_spec.get()); + } + + return exe_file_spec; +} + +bool +SBTarget::operator == (const SBTarget &rhs) const +{ + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); +} + +bool +SBTarget::operator != (const SBTarget &rhs) const +{ + 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::SBAddress sb_addr; + Address &addr = sb_addr.ref(); + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + if (target_sp->GetSectionLoadList().ResolveLoadAddress (vm_addr, addr)) + return 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 sb_addr; +} + +SBSymbolContext +SBTarget::ResolveSymbolContextForAddress (const SBAddress& addr, uint32_t resolve_scope) +{ + SBSymbolContext sc; + if (addr.IsValid()) + { + TargetSP target_sp(GetSP()); + if (target_sp) + target_sp->GetImages().ResolveSymbolContextForAddress (addr.ref(), resolve_scope, sc.ref()); + } + return sc; +} + + +SBBreakpoint +SBTarget::BreakpointCreateByLocation (const char *file, uint32_t line) +{ + return SBBreakpoint(BreakpointCreateByLocation (SBFileSpec (file, false), line)); +} + +SBBreakpoint +SBTarget::BreakpointCreateByLocation (const SBFileSpec &sb_file_spec, uint32_t line) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && line != 0) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + const LazyBool check_inlines = eLazyBoolCalculate; + const LazyBool skip_prologue = eLazyBoolCalculate; + const bool internal = false; + *sb_bp = target_sp->CreateBreakpoint (NULL, *sb_file_spec, line, check_inlines, skip_prologue, internal); + } + + if (log) + { + SBStream sstr; + sb_bp.GetDescription (sstr); + char path[PATH_MAX]; + sb_file_spec->GetPath (path, sizeof(path)); + log->Printf ("SBTarget(%p)::BreakpointCreateByLocation ( %s:%u ) => SBBreakpoint(%p): %s", + target_sp.get(), + path, + line, + sb_bp.get(), + sstr.GetData()); + } + + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByName (const char *symbol_name, const char *module_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp.get()) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + if (module_name && module_name[0]) + { + FileSpecList module_spec_list; + module_spec_list.Append (FileSpec (module_name, false)); + *sb_bp = target_sp->CreateBreakpoint (&module_spec_list, NULL, symbol_name, eFunctionNameTypeAuto, skip_prologue, internal); + } + else + { + *sb_bp = target_sp->CreateBreakpoint (NULL, NULL, symbol_name, eFunctionNameTypeAuto, skip_prologue, internal); + } + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByName (symbol=\"%s\", module=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), symbol_name, module_name, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateByName (const char *symbol_name, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) +{ + uint32_t name_type_mask = eFunctionNameTypeAuto; + return BreakpointCreateByName (symbol_name, name_type_mask, 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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && symbol_name && symbol_name[0]) + { + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_bp = target_sp->CreateBreakpoint (module_list.get(), + comp_unit_list.get(), + symbol_name, + name_type_mask, + skip_prologue, + internal); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByName (symbol=\"%s\", name_type: %d) => SBBreakpoint(%p)", + target_sp.get(), symbol_name, name_type_mask, sb_bp.get()); + } + + return 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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && num_names > 0) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + *sb_bp = target_sp->CreateBreakpoint (module_list.get(), + comp_unit_list.get(), + symbol_names, + num_names, + name_type_mask, + skip_prologue, + internal); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByName (symbols={", target_sp.get()); + for (uint32_t i = 0 ; i < num_names; i++) + { + char sep; + if (i < num_names - 1) + sep = ','; + else + sep = '}'; + if (symbol_names[i] != NULL) + log->Printf ("\"%s\"%c ", symbol_names[i], sep); + else + log->Printf ("\"\"%c ", sep); + + } + log->Printf ("name_type: %d) => SBBreakpoint(%p)", name_type_mask, sb_bp.get()); + } + + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByRegex (const char *symbol_name_regex, const char *module_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && symbol_name_regex && symbol_name_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(symbol_name_regex); + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + + if (module_name && module_name[0]) + { + FileSpecList module_spec_list; + module_spec_list.Append (FileSpec (module_name, false)); + + *sb_bp = target_sp->CreateFuncRegexBreakpoint (&module_spec_list, NULL, regexp, skip_prologue, internal); + } + else + { + *sb_bp = target_sp->CreateFuncRegexBreakpoint (NULL, NULL, regexp, skip_prologue, internal); + } + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (symbol_regex=\"%s\", module_name=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), symbol_name_regex, module_name, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateByRegex (const char *symbol_name_regex, + const SBFileSpecList &module_list, + const SBFileSpecList &comp_unit_list) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && symbol_name_regex && symbol_name_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(symbol_name_regex); + const bool internal = false; + const LazyBool skip_prologue = eLazyBoolCalculate; + + *sb_bp = target_sp->CreateFuncRegexBreakpoint (module_list.get(), comp_unit_list.get(), regexp, skip_prologue, internal); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (symbol_regex=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), symbol_name_regex, sb_bp.get()); + } + + return sb_bp; +} + +SBBreakpoint +SBTarget::BreakpointCreateByAddress (addr_t address) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_bp = target_sp->CreateBreakpoint (address, false); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByAddress (address=%" PRIu64 ") => SBBreakpoint(%p)", target_sp.get(), (uint64_t) address, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateBySourceRegex (const char *source_regex, const lldb::SBFileSpec &source_file, const char *module_name) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && source_regex && source_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(source_regex); + FileSpecList source_file_spec_list; + source_file_spec_list.Append (source_file.ref()); + + if (module_name && module_name[0]) + { + FileSpecList module_spec_list; + module_spec_list.Append (FileSpec (module_name, false)); + + *sb_bp = target_sp->CreateSourceRegexBreakpoint (&module_spec_list, &source_file_spec_list, regexp, false); + } + else + { + *sb_bp = target_sp->CreateSourceRegexBreakpoint (NULL, &source_file_spec_list, regexp, false); + } + } + + if (log) + { + char path[PATH_MAX]; + source_file->GetPath (path, sizeof(path)); + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (source_regex=\"%s\", file=\"%s\", module_name=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), source_regex, path, module_name, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateBySourceRegex (const char *source_regex, + const SBFileSpecList &module_list, + const lldb::SBFileSpecList &source_file_list) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp && source_regex && source_regex[0]) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + RegularExpression regexp(source_regex); + *sb_bp = target_sp->CreateSourceRegexBreakpoint (module_list.get(), source_file_list.get(), regexp, false); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (source_regex=\"%s\") => SBBreakpoint(%p)", + target_sp.get(), source_regex, sb_bp.get()); + } + + return sb_bp; +} + +lldb::SBBreakpoint +SBTarget::BreakpointCreateForException (lldb::LanguageType language, + bool catch_bp, + bool throw_bp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_bp; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_bp = target_sp->CreateExceptionBreakpoint (language, catch_bp, throw_bp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointCreateByRegex (Language: %s, catch: %s throw: %s) => SBBreakpoint(%p)", + target_sp.get(), + LanguageRuntime::GetNameForLanguageType(language), + catch_bp ? "on" : "off", + throw_bp ? "on" : "off", + sb_bp.get()); + } + + return sb_bp; +} + +uint32_t +SBTarget::GetNumBreakpoints () const +{ + 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 +{ + 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 sb_breakpoint; +} + +bool +SBTarget::BreakpointDelete (break_id_t bp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = false; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + result = target_sp->RemoveBreakpointByID (bp_id); + } + + if (log) + { + log->Printf ("SBTarget(%p)::BreakpointDelete (bp_id=%d) => %i", target_sp.get(), (uint32_t) bp_id, result); + } + + return result; +} + +SBBreakpoint +SBTarget::FindBreakpointByID (break_id_t bp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBBreakpoint sb_breakpoint; + TargetSP target_sp(GetSP()); + if (target_sp && bp_id != LLDB_INVALID_BREAK_ID) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + *sb_breakpoint = target_sp->GetBreakpointByID (bp_id); + } + + if (log) + { + log->Printf ("SBTarget(%p)::FindBreakpointByID (bp_id=%d) => SBBreakpoint(%p)", + target_sp.get(), (uint32_t) bp_id, sb_breakpoint.get()); + } + + return sb_breakpoint; +} + +bool +SBTarget::EnableAllBreakpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + target_sp->EnableAllBreakpoints (); + return true; + } + return false; +} + +bool +SBTarget::DisableAllBreakpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + target_sp->DisableAllBreakpoints (); + return true; + } + return false; +} + +bool +SBTarget::DeleteAllBreakpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + target_sp->RemoveAllBreakpoints (); + return true; + } + return false; +} + +uint32_t +SBTarget::GetNumWatchpoints () const +{ + 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 +{ + 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 sb_watchpoint; +} + +bool +SBTarget::DeleteWatchpoint (watch_id_t wp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + bool result = false; + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + result = target_sp->RemoveWatchpointByID (wp_id); + } + + if (log) + { + log->Printf ("SBTarget(%p)::WatchpointDelete (wp_id=%d) => %i", target_sp.get(), (uint32_t) wp_id, result); + } + + return result; +} + +SBWatchpoint +SBTarget::FindWatchpointByID (lldb::watch_id_t wp_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBWatchpoint sb_watchpoint; + lldb::WatchpointSP watchpoint_sp; + TargetSP target_sp(GetSP()); + if (target_sp && wp_id != LLDB_INVALID_WATCH_ID) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + watchpoint_sp = target_sp->GetWatchpointList().FindByID(wp_id); + sb_watchpoint.SetSP (watchpoint_sp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::FindWatchpointByID (bp_id=%d) => SBWatchpoint(%p)", + target_sp.get(), (uint32_t) wp_id, watchpoint_sp.get()); + } + + return sb_watchpoint; +} + +lldb::SBWatchpoint +SBTarget::WatchAddress (lldb::addr_t addr, size_t size, bool read, bool write, SBError &error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBWatchpoint sb_watchpoint; + lldb::WatchpointSP watchpoint_sp; + TargetSP target_sp(GetSP()); + if (target_sp && (read || write) && addr != LLDB_INVALID_ADDRESS && size > 0) + { + Mutex::Locker api_locker (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 sb_watchpoint; + } + + // Target::CreateWatchpoint() is thread safe. + Error cw_error; + // This API doesn't take in a type, so we can't figure out what it is. + ClangASTType *type = NULL; + watchpoint_sp = target_sp->CreateWatchpoint(addr, size, type, watch_type, cw_error); + error.SetError(cw_error); + sb_watchpoint.SetSP (watchpoint_sp); + } + + if (log) + { + log->Printf ("SBTarget(%p)::WatchAddress (addr=0x%" PRIx64 ", 0x%u) => SBWatchpoint(%p)", + target_sp.get(), addr, (uint32_t) size, watchpoint_sp.get()); + } + + return sb_watchpoint; +} + +bool +SBTarget::EnableAllWatchpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + target_sp->EnableAllWatchpoints (); + return true; + } + return false; +} + +bool +SBTarget::DisableAllWatchpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + target_sp->DisableAllWatchpoints (); + return true; + } + return false; +} + +bool +SBTarget::DeleteAllWatchpoints () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + Mutex::Locker locker; + target_sp->GetWatchpointList().GetListMutex(locker); + target_sp->RemoveAllWatchpoints (); + return true; + } + return false; +} + + +lldb::SBModule +SBTarget::AddModule (const char *path, + const char *triple, + const char *uuid_cstr) +{ + return AddModule (path, triple, uuid_cstr, NULL); +} + +lldb::SBModule +SBTarget::AddModule (const char *path, + const char *triple, + const char *uuid_cstr, + const char *symfile) +{ + lldb::SBModule sb_module; + TargetSP target_sp(GetSP()); + if (target_sp) + { + ModuleSpec module_spec; + if (path) + module_spec.GetFileSpec().SetFile(path, false); + + if (uuid_cstr) + module_spec.GetUUID().SetFromCString(uuid_cstr); + + if (triple) + module_spec.GetArchitecture().SetTriple (triple, target_sp->GetPlatform ().get()); + + if (symfile) + module_spec.GetSymbolFileSpec ().SetFile(symfile, false); + + sb_module.SetSP(target_sp->GetSharedModule (module_spec)); + } + return sb_module; +} + +lldb::SBModule +SBTarget::AddModule (const SBModuleSpec &module_spec) +{ + lldb::SBModule sb_module; + TargetSP target_sp(GetSP()); + if (target_sp) + sb_module.SetSP(target_sp->GetSharedModule (*module_spec.m_opaque_ap)); + return sb_module; +} + +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 +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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(); + } + + if (log) + log->Printf ("SBTarget(%p)::GetNumModules () => %d", target_sp.get(), num); + + return num; +} + +void +SBTarget::Clear () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + log->Printf ("SBTarget(%p)::Clear ()", m_opaque_sp.get()); + + m_opaque_sp.reset(); +} + + +SBModule +SBTarget::FindModule (const 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 sb_module; +} + +lldb::ByteOrder +SBTarget::GetByteOrder () +{ + TargetSP target_sp(GetSP()); + if (target_sp) + return target_sp->GetArchitecture().GetByteOrder(); + return eByteOrderInvalid; +} + +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 NULL; +} + +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) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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); + } + + if (log) + { + log->Printf ("SBTarget(%p)::GetModuleAtIndex (idx=%d) => SBModule(%p)", + target_sp.get(), idx, module_sp.get()); + } + + return sb_module; +} + +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 +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + TargetSP target_sp(GetSP()); + SBBroadcaster broadcaster(target_sp.get(), false); + + if (log) + log->Printf ("SBTarget(%p)::GetBroadcaster () => SBBroadcaster(%p)", + target_sp.get(), broadcaster.get()); + + return broadcaster; +} + +bool +SBTarget::GetDescription (SBStream &description, lldb::DescriptionLevel 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::SBSymbolContextList sb_sc_list; + if (name && name[0]) + { + TargetSP target_sp(GetSP()); + if (target_sp) + { + const bool symbols_ok = true; + const bool inlines_ok = true; + const bool append = true; + target_sp->GetImages().FindFunctions (ConstString(name), + name_type_mask, + symbols_ok, + inlines_ok, + append, + *sb_sc_list); + } + } + return sb_sc_list; +} + +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 SBType(type_sp); + } + } + + // Didn't find the type in the symbols; try the Objective-C runtime + // if one is installed + + ProcessSP process_sp(target_sp->GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime = process_sp->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + TypeVendor *objc_type_vendor = objc_language_runtime->GetTypeVendor(); + + if (objc_type_vendor) + { + std::vector types; + + if (objc_type_vendor->FindTypes(const_typename, true, 1, types) > 0) + return SBType(types[0]); + } + } + } + + // No matches, search for basic typename matches + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + return SBType (ClangASTContext::GetBasicType (clang_ast->getASTContext(), const_typename)); + } + return SBType(); +} + +SBType +SBTarget::GetBasicType(lldb::BasicType type) +{ + TargetSP target_sp(GetSP()); + if (target_sp) + { + ClangASTContext *clang_ast = target_sp->GetScratchClangASTContext(); + if (clang_ast) + return SBType (ClangASTContext::GetBasicType (clang_ast->getASTContext(), type)); + } + return SBType(); +} + + +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; + SymbolContext sc; + TypeList type_list; + + uint32_t num_matches = images.FindTypes (sc, + const_typename, + exact_match, + UINT32_MAX, + 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 Objective-C runtime if one is installed + + ProcessSP process_sp(target_sp->GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime = process_sp->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + TypeVendor *objc_type_vendor = objc_language_runtime->GetTypeVendor(); + + if (objc_type_vendor) + { + std::vector types; + + if (objc_type_vendor->FindTypes(const_typename, true, UINT32_MAX, types)) + { + for (ClangASTType &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 sb_type_list; +} + +SBValueList +SBTarget::FindGlobalVariables (const char *name, uint32_t max_matches) +{ + SBValueList sb_value_list; + + TargetSP target_sp(GetSP()); + if (name && target_sp) + { + VariableList variable_list; + const bool append = true; + const uint32_t match_count = target_sp->GetImages().FindGlobalVariables (ConstString (name), + append, + max_matches, + variable_list); + + if (match_count > 0) + { + ExecutionContextScope *exe_scope = target_sp->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target_sp.get(); + for (uint32_t i=0; i 0) + return sb_value_list.GetValueAtIndex(0); + return SBValue(); +} + +SBSourceManager +SBTarget::GetSourceManager() +{ + SBSourceManager source_manager (*this); + return source_manager; +} + +lldb::SBInstructionList +SBTarget::ReadInstructions (lldb::SBAddress base_addr, uint32_t count) +{ + return ReadInstructions (base_addr, count, NULL); +} + +lldb::SBInstructionList +SBTarget::ReadInstructions (lldb::SBAddress base_addr, uint32_t count, const char *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::Error 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(), + NULL, + flavor_string, + *addr_ptr, + data.GetBytes(), + bytes_read, + count, + data_from_file)); + } + } + + return sb_instructions; + +} + +lldb::SBInstructionList +SBTarget::GetInstructions (lldb::SBAddress base_addr, const void *buf, size_t size) +{ + return GetInstructionsWithFlavor (base_addr, NULL, buf, size); +} + +lldb::SBInstructionList +SBTarget::GetInstructionsWithFlavor (lldb::SBAddress base_addr, const char *flavor_string, const void *buf, size_t 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(), + NULL, + 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) +{ + return GetInstructionsWithFlavor (ResolveLoadAddress(base_addr), NULL, buf, size); +} + +lldb::SBInstructionList +SBTarget::GetInstructionsWithFlavor (lldb::addr_t base_addr, const char *flavor_string, const void *buf, size_t size) +{ + return GetInstructionsWithFlavor (ResolveLoadAddress(base_addr), flavor_string, buf, size); +} + +SBError +SBTarget::SetSectionLoadAddress (lldb::SBSection section, + lldb::addr_t 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 + { + if (target_sp->GetSectionLoadList().SetSectionLoadAddress (section_sp, section_base_addr)) + { + // Flush info in the process (stack frames, etc) + ProcessSP process_sp (target_sp->GetProcessSP()); + if (process_sp) + process_sp->Flush(); + } + } + } + } + } + else + { + sb_error.SetErrorString ("invalid target"); + } + return sb_error; +} + +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 + { + if (target_sp->GetSectionLoadList().SetSectionUnloaded (section.GetSP())) + { + // 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 target"); + } + return sb_error; +} + +SBError +SBTarget::SetModuleLoadAddress (lldb::SBModule module, int64_t 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, 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 sb_error; +} + +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) + { + 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->GetSectionLoadList().SetSectionUnloaded (section_sp) > 0; + } + if (changed) + { + // 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 sb_error; +} + + +lldb::SBSymbolContextList +SBTarget::FindSymbols (const char *name, lldb::SymbolType 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 sb_sc_list; + +} + + +lldb::SBValue +SBTarget::EvaluateExpression (const char *expr, const SBExpressionOptions &options) +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + Log * expr_log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + SBValue expr_result; + ExecutionResults exe_results = eExecutionSetupError; + ValueObjectSP expr_value_sp; + TargetSP target_sp(GetSP()); + StackFrame *frame = NULL; + if (target_sp) + { + if (expr == NULL || expr[0] == '\0') + { + if (log) + log->Printf ("SBTarget::EvaluateExpression called with an empty expression"); + return expr_result; + } + + Mutex::Locker api_locker (target_sp->GetAPIMutex()); + ExecutionContext exe_ctx (m_opaque_sp.get()); + + if (log) + log->Printf ("SBTarget()::EvaluateExpression (expr=\"%s\")...", expr); + + frame = exe_ctx.GetFramePtr(); + Target *target = exe_ctx.GetTargetPtr(); + + if (target) + { +#ifdef LLDB_CONFIGURATION_DEBUG + StreamString frame_description; + if (frame) + frame->DumpUsingSettingsFormat (&frame_description); + Host::SetCrashDescriptionWithFormat ("SBTarget::EvaluateExpression (expr = \"%s\", fetch_dynamic_value = %u) %s", + expr, options.GetFetchDynamicValue(), frame_description.GetString().c_str()); +#endif + exe_results = target->EvaluateExpression (expr, + frame, + expr_value_sp, + options.ref()); + + expr_result.SetSP(expr_value_sp, options.GetFetchDynamicValue()); +#ifdef LLDB_CONFIGURATION_DEBUG + Host::SetCrashDescription (NULL); +#endif + } + else + { + if (log) + log->Printf ("SBTarget::EvaluateExpression () => error: could not reconstruct frame object for this SBTarget."); + } + } +#ifndef LLDB_DISABLE_PYTHON + if (expr_log) + expr_log->Printf("** [SBTarget::EvaluateExpression] Expression result is %s, summary %s **", + expr_result.GetValue(), + expr_result.GetSummary()); + + if (log) + log->Printf ("SBTarget(%p)::EvaluateExpression (expr=\"%s\") => SBValue(%p) (execution result=%d)", + frame, + expr, + expr_value_sp.get(), + exe_results); +#endif + + return expr_result; +} + + +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(target_sp->GetArchitecture()); + if (abi_sp) + return abi_sp->GetRedZoneSize(); + } + return 0; +} + diff --git a/source/API/SBThread.cpp b/source/API/SBThread.cpp new file mode 100644 index 00000000000..2752620c9ba --- /dev/null +++ b/source/API/SBThread.cpp @@ -0,0 +1,1229 @@ +//===-- SBThread.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBThread.h" + +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBStream.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/Process.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadPlan.h" +#include "lldb/Target/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.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/SBValue.h" + +using namespace lldb; +using namespace lldb_private; + +const char * +SBThread::GetBroadcasterClassName () +{ + return Thread::GetStaticBroadcasterClass().AsCString(); +} + +//---------------------------------------------------------------------- +// Constructors +//---------------------------------------------------------------------- +SBThread::SBThread () : + m_opaque_sp (new ExecutionContextRef()) +{ +} + +SBThread::SBThread (const ThreadSP& lldb_object_sp) : + m_opaque_sp (new ExecutionContextRef(lldb_object_sp)) +{ +} + +SBThread::SBThread (const SBThread &rhs) : + m_opaque_sp (new ExecutionContextRef(*rhs.m_opaque_sp)) +{ + +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- + +const lldb::SBThread & +SBThread::operator = (const SBThread &rhs) +{ + if (this != &rhs) + *m_opaque_sp = *rhs.m_opaque_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SBThread::~SBThread() +{ +} + +bool +SBThread::IsValid() const +{ + return m_opaque_sp->GetThreadSP().get() != NULL; +} + +void +SBThread::Clear () +{ + m_opaque_sp->Clear(); +} + + +StopReason +SBThread::GetStopReason() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + StopReason reason = eStopReasonInvalid; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + return exe_ctx.GetThreadPtr()->GetStopReason(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetStopReason() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetStopReason () => %s", exe_ctx.GetThreadPtr(), + Thread::StopReasonAsCString (reason)); + + return reason; +} + +size_t +SBThread::GetStopReasonDataCount () +{ + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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: + // 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; + } + } + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBThread(%p)::GetStopReasonDataCount() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + return 0; +} + +uint64_t +SBThread::GetStopReasonDataAtIndex (uint32_t idx) +{ + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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: + // 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 (bp_index & 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(); + } + } + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBThread(%p)::GetStopReasonDataAtIndex() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + return 0; +} + +size_t +SBThread::GetStopDescription (char *dst, size_t dst_len) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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 (log) + log->Printf ("SBThread(%p)::GetStopDescription (dst, dst_len) => \"%s\"", + exe_ctx.GetThreadPtr(), 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 == NULL || 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 (log) + log->Printf ("SBThread(%p)::GetStopDescription (dst, dst_len) => '%s'", + exe_ctx.GetThreadPtr(), stop_desc); + + 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; + } + } + } + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBThread(%p)::GetStopDescription() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + if (dst) + *dst = 0; + return 0; +} + +SBValue +SBThread::GetStopReturnValue () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueObjectSP return_valobj_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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); + } + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetStopReturnValue() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetStopReturnValue () => %s", exe_ctx.GetThreadPtr(), + return_valobj_sp.get() + ? return_valobj_sp->GetValueAsCString() + : ""); + + return 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 +{ + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetID(); + return LLDB_INVALID_THREAD_ID; +} + +uint32_t +SBThread::GetIndexID () const +{ + ThreadSP thread_sp(m_opaque_sp->GetThreadSP()); + if (thread_sp) + return thread_sp->GetIndexID(); + return LLDB_INVALID_INDEX32; +} + +const char * +SBThread::GetName () const +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *name = NULL; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + name = exe_ctx.GetThreadPtr()->GetName(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetName() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetName () => %s", exe_ctx.GetThreadPtr(), name ? name : "NULL"); + + return name; +} + +const char * +SBThread::GetQueueName () const +{ + const char *name = NULL; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + name = exe_ctx.GetThreadPtr()->GetQueueName(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetQueueName() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetQueueName () => %s", exe_ctx.GetThreadPtr(), name ? name : "NULL"); + + return name; +} + +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 != NULL) + { + 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()); + sb_error.ref() = process->Resume(); + + if (sb_error.Success()) + { + // If we are doing synchronous mode, then wait for the + // process to stop yet again! + if (process->GetTarget().GetDebugger().GetAsyncExecution () == false) + process->WaitForProcessToStop (NULL); + } + + return sb_error; +} + +void +SBThread::StepOver (lldb::RunMode stop_other_threads) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::StepOver (stop_other_threads='%s')", exe_ctx.GetThreadPtr(), + Thread::RunModeAsCString (stop_other_threads)); + + if (exe_ctx.HasThreadScope()) + { + Thread *thread = exe_ctx.GetThreadPtr(); + bool abort_other_plans = false; + StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); + + ThreadPlanSP new_plan_sp; + if (frame_sp) + { + if (frame_sp->HasDebugInformation ()) + { + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = thread->QueueThreadPlanForStepOverRange (abort_other_plans, + sc.line_entry.range, + sc, + stop_other_threads); + } + else + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (true, + abort_other_plans, + stop_other_threads); + } + } + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepInto (lldb::RunMode stop_other_threads) +{ + StepInto (NULL, stop_other_threads); +} + +void +SBThread::StepInto (const char *target_name, lldb::RunMode stop_other_threads) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (log) + log->Printf ("SBThread(%p)::StepInto (target_name='%s', stop_other_threads='%s')", + exe_ctx.GetThreadPtr(), + target_name? target_name: "", + Thread::RunModeAsCString (stop_other_threads)); + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + + Thread *thread = exe_ctx.GetThreadPtr(); + StackFrameSP frame_sp(thread->GetStackFrameAtIndex (0)); + ThreadPlanSP new_plan_sp; + + if (frame_sp && frame_sp->HasDebugInformation ()) + { + bool avoid_code_without_debug_info = true; + SymbolContext sc(frame_sp->GetSymbolContext(eSymbolContextEverything)); + new_plan_sp = thread->QueueThreadPlanForStepInRange (abort_other_plans, + sc.line_entry.range, + sc, + target_name, + stop_other_threads, + avoid_code_without_debug_info); + } + else + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (false, + abort_other_plans, + stop_other_threads); + } + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepOut () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::StepOut ()", exe_ctx.GetThreadPtr()); + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + bool stop_other_threads = false; + + Thread *thread = exe_ctx.GetThreadPtr(); + + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut (abort_other_plans, + NULL, + false, + stop_other_threads, + eVoteYes, + eVoteNoOpinion, + 0)); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepOutOfFrame (lldb::SBFrame &sb_frame) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrameSP frame_sp (sb_frame.GetFrameSP()); + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::StepOutOfFrame (frame = SBFrame(%p): %s)", exe_ctx.GetThreadPtr(), frame_sp.get(), frame_desc_strm.GetData()); + } + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + bool stop_other_threads = false; + Thread *thread = exe_ctx.GetThreadPtr(); + + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepOut (abort_other_plans, + NULL, + false, + stop_other_threads, + eVoteYes, + eVoteNoOpinion, + frame_sp->GetFrameIndex())); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::StepInstruction (bool step_over) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + + if (log) + log->Printf ("SBThread(%p)::StepInstruction (step_over=%i)", exe_ctx.GetThreadPtr(), step_over); + + if (exe_ctx.HasThreadScope()) + { + Thread *thread = exe_ctx.GetThreadPtr(); + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForStepSingleInstruction (step_over, true, true)); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +void +SBThread::RunToAddress (lldb::addr_t addr) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::RunToAddress (addr=0x%" PRIx64 ")", exe_ctx.GetThreadPtr(), addr); + + if (exe_ctx.HasThreadScope()) + { + bool abort_other_plans = false; + bool stop_other_threads = true; + + Address target_addr (addr); + + Thread *thread = exe_ctx.GetThreadPtr(); + + ThreadPlanSP new_plan_sp(thread->QueueThreadPlanForRunToAddress (abort_other_plans, target_addr, stop_other_threads)); + + // This returns an error, we should use it! + ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } +} + +SBError +SBThread::StepOverUntil (lldb::SBFrame &sb_frame, + lldb::SBFileSpec &sb_file_spec, + uint32_t line) +{ + SBError sb_error; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + char path[PATH_MAX]; + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + StackFrameSP frame_sp (sb_frame.GetFrameSP()); + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + sb_file_spec->GetPath (path, sizeof(path)); + log->Printf ("SBThread(%p)::StepOverUntil (frame = SBFrame(%p): %s, file+line = %s:%u)", + exe_ctx.GetThreadPtr(), + frame_sp.get(), + frame_desc_strm.GetData(), + path, line); + } + + if (exe_ctx.HasThreadScope()) + { + Target *target = exe_ctx.GetTargetPtr(); + Thread *thread = exe_ctx.GetThreadPtr(); + + if (line == 0) + { + sb_error.SetErrorString("invalid line argument"); + return 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 sb_error; + } + + // If we have a frame, get its line + frame_sc = frame_sp->GetSymbolContext (eSymbolContextCompUnit | + eSymbolContextFunction | + eSymbolContextLineEntry | + eSymbolContextSymbol ); + + if (frame_sc.comp_unit == NULL) + { + sb_error.SetErrorStringWithFormat("frame %u doesn't have debug information", frame_sp->GetFrameIndex()); + return 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 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 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; iQueueThreadPlanForStepUntil (abort_other_plans, + &step_over_until_addrs[0], + step_over_until_addrs.size(), + stop_other_threads, + frame_sp->GetFrameIndex())); + + sb_error = ResumeNewPlan (exe_ctx, new_plan_sp.get()); + } + } + else + { + sb_error.SetErrorString("this SBThread object is invalid"); + } + return sb_error; +} + +SBError +SBThread::ReturnFromFrame (SBFrame &frame, SBValue &return_value) +{ + SBError sb_error; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + + if (log) + log->Printf ("SBThread(%p)::ReturnFromFrame (frame=%d)", exe_ctx.GetThreadPtr(), frame.GetFrameID()); + + if (exe_ctx.HasThreadScope()) + { + Thread *thread = exe_ctx.GetThreadPtr(); + sb_error.SetError (thread->ReturnFromFrame(frame.GetFrameSP(), return_value.GetSP())); + } + + return sb_error; +} + + +bool +SBThread::Suspend() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ExecutionContext exe_ctx (m_opaque_sp.get()); + 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 + { + if (log) + log->Printf ("SBThread(%p)::Suspend() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + if (log) + log->Printf ("SBThread(%p)::Suspend() => %i", exe_ctx.GetThreadPtr(), result); + return result; +} + +bool +SBThread::Resume () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ExecutionContext exe_ctx (m_opaque_sp.get()); + bool result = false; + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + exe_ctx.GetThreadPtr()->SetResumeState (eStateRunning); + result = true; + } + else + { + if (log) + log->Printf ("SBThread(%p)::Resume() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + if (log) + log->Printf ("SBThread(%p)::Resume() => %i", exe_ctx.GetThreadPtr(), result); + return result; +} + +bool +SBThread::IsSuspended() +{ + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + return exe_ctx.GetThreadPtr()->GetResumeState () == eStateSuspended; + return false; +} + +bool +SBThread::IsStopped() +{ + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + return StateIsStoppedState(exe_ctx.GetThreadPtr()->GetState(), true); + return false; +} + +SBProcess +SBThread::GetProcess () +{ + SBProcess sb_process; + ExecutionContext exe_ctx (m_opaque_sp.get()); + 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()); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + SBStream frame_desc_strm; + sb_process.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::GetProcess () => SBProcess(%p): %s", exe_ctx.GetThreadPtr(), + sb_process.GetSP().get(), frame_desc_strm.GetData()); + } + + return sb_process; +} + +uint32_t +SBThread::GetNumFrames () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + uint32_t num_frames = 0; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + if (exe_ctx.HasThreadScope()) + { + Process::StopLocker stop_locker; + if (stop_locker.TryLock(&exe_ctx.GetProcessPtr()->GetRunLock())) + { + num_frames = exe_ctx.GetThreadPtr()->GetStackFrameCount(); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetNumFrames() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + log->Printf ("SBThread(%p)::GetNumFrames () => %u", exe_ctx.GetThreadPtr(), num_frames); + + return num_frames; +} + +SBFrame +SBThread::GetFrameAtIndex (uint32_t idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFrame sb_frame; + StackFrameSP frame_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetFrameAtIndex() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::GetFrameAtIndex (idx=%d) => SBFrame(%p): %s", + exe_ctx.GetThreadPtr(), idx, frame_sp.get(), frame_desc_strm.GetData()); + } + + return sb_frame; +} + +lldb::SBFrame +SBThread::GetSelectedFrame () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFrame sb_frame; + StackFrameSP frame_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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); + } + else + { + if (log) + log->Printf ("SBThread(%p)::GetSelectedFrame() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::GetSelectedFrame () => SBFrame(%p): %s", + exe_ctx.GetThreadPtr(), frame_sp.get(), frame_desc_strm.GetData()); + } + + return sb_frame; +} + +lldb::SBFrame +SBThread::SetSelectedFrame (uint32_t idx) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + SBFrame sb_frame; + StackFrameSP frame_sp; + Mutex::Locker api_locker; + ExecutionContext exe_ctx (m_opaque_sp.get(), api_locker); + + 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); + } + } + else + { + if (log) + log->Printf ("SBThread(%p)::SetSelectedFrame() => error: process is running", exe_ctx.GetThreadPtr()); + } + } + + if (log) + { + SBStream frame_desc_strm; + sb_frame.GetDescription (frame_desc_strm); + log->Printf ("SBThread(%p)::SetSelectedFrame (idx=%u) => SBFrame(%p): %s", + exe_ctx.GetThreadPtr(), idx, frame_sp.get(), frame_desc_strm.GetData()); + } + return sb_frame; +} + +bool +SBThread::EventIsThreadEvent (const SBEvent &event) +{ + return Thread::ThreadEventData::GetEventDataFromEvent(event.get()) != NULL; +} + +SBFrame +SBThread::GetStackFrameFromEvent (const SBEvent &event) +{ + return Thread::ThreadEventData::GetStackFrameFromEvent (event.get()); + +} + +SBThread +SBThread::GetThreadFromEvent (const SBEvent &event) +{ + return Thread::ThreadEventData::GetThreadFromEvent (event.get()); +} + +bool +SBThread::operator == (const SBThread &rhs) const +{ + return m_opaque_sp->GetThreadSP().get() == rhs.m_opaque_sp->GetThreadSP().get(); +} + +bool +SBThread::operator != (const SBThread &rhs) const +{ + return m_opaque_sp->GetThreadSP().get() != rhs.m_opaque_sp->GetThreadSP().get(); +} + +bool +SBThread::GetStatus (SBStream &status) const +{ + Stream &strm = status.ref(); + + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + { + exe_ctx.GetThreadPtr()->GetStatus(strm, 0, 1, 1); + } + else + strm.PutCString ("No status"); + + return true; +} + +bool +SBThread::GetDescription (SBStream &description) const +{ + Stream &strm = description.ref(); + + ExecutionContext exe_ctx (m_opaque_sp.get()); + if (exe_ctx.HasThreadScope()) + { + strm.Printf("SBThread: tid = 0x%4.4" PRIx64, exe_ctx.GetThreadPtr()->GetID()); + } + else + strm.PutCString ("No value"); + + return true; +} diff --git a/source/API/SBType.cpp b/source/API/SBType.cpp new file mode 100644 index 00000000000..372d073acf1 --- /dev/null +++ b/source/API/SBType.cpp @@ -0,0 +1,648 @@ +//===-- SBType.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "clang/AST/ASTContext.h" +#include "clang/AST/TemplateBase.h" +#include "clang/AST/Type.h" + +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/Type.h" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +SBType::SBType() : + m_opaque_sp() +{ +} + +SBType::SBType (const ClangASTType &type) : + m_opaque_sp(new TypeImpl(ClangASTType(type.GetASTContext(), + 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() +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } +} + + +//SBType::SBType (TypeImpl* impl) : +// m_opaque_ap(impl) +//{} +// +bool +SBType::operator == (SBType &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return (rhs.m_opaque_sp->GetASTContext() == m_opaque_sp->GetASTContext()) && + (rhs.m_opaque_sp->GetOpaqueQualType() == m_opaque_sp->GetOpaqueQualType()); +} + +bool +SBType::operator != (SBType &rhs) +{ + if (IsValid() == false) + return rhs.IsValid(); + + return (rhs.m_opaque_sp->GetASTContext() != m_opaque_sp->GetASTContext()) || + (rhs.m_opaque_sp->GetOpaqueQualType() != m_opaque_sp->GetOpaqueQualType()); +} + +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) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +SBType::~SBType () +{} + +TypeImpl & +SBType::ref () +{ + if (m_opaque_sp.get() == NULL) + m_opaque_sp.reset (new 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 +{ + if (m_opaque_sp.get() == NULL) + return false; + + return m_opaque_sp->IsValid(); +} + +uint64_t +SBType::GetByteSize() +{ + if (!IsValid()) + return 0; + + return m_opaque_sp->GetClangASTType().GetByteSize(); + +} + +bool +SBType::IsPointerType() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsPointerType(); +} + +bool +SBType::IsReferenceType() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsReferenceType(); +} + +SBType +SBType::GetPointerType() +{ + if (!IsValid()) + return SBType(); + + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetPointerType())); +} + +SBType +SBType::GetPointeeType() +{ + if (!IsValid()) + return SBType(); + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetPointeeType())); +} + +SBType +SBType::GetReferenceType() +{ + if (!IsValid()) + return SBType(); + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetLValueReferenceType())); +} + +SBType +SBType::GetDereferencedType() +{ + if (!IsValid()) + return SBType(); + return SBType(ClangASTType(m_opaque_sp->GetClangASTType().GetNonReferenceType())); +} + +bool +SBType::IsFunctionType () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsFunctionType(); +} + +bool +SBType::IsPolymorphicClass () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsPolymorphicClass(); +} + + + +lldb::SBType +SBType::GetFunctionReturnType () +{ + if (IsValid()) + { + ClangASTType return_clang_type (m_opaque_sp->GetClangASTType().GetFunctionReturnType()); + if (return_clang_type.IsValid()) + return SBType(return_clang_type); + } + return lldb::SBType(); +} + +lldb::SBTypeList +SBType::GetFunctionArgumentTypes () +{ + SBTypeList sb_type_list; + if (IsValid()) + { + QualType qual_type(QualType::getFromOpaquePtr(m_opaque_sp->GetOpaqueQualType())); + const FunctionProtoType* func = dyn_cast(qual_type.getTypePtr()); + if (func) + { + const uint32_t num_args = func->getNumArgs(); + for (uint32_t i=0; iGetASTContext(), func->getArgType(i).getAsOpaquePtr()))); + } + } + return sb_type_list; +} + +lldb::SBType +SBType::GetUnqualifiedType() +{ + if (!IsValid()) + return SBType(); + return SBType(m_opaque_sp->GetClangASTType().GetFullyUnqualifiedType()); +} + +lldb::SBType +SBType::GetCanonicalType() +{ + if (IsValid()) + return SBType(m_opaque_sp->GetClangASTType().GetCanonicalType()); + return SBType(); +} + + +lldb::BasicType +SBType::GetBasicType() +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetBasicTypeEnumeration (); + return eBasicTypeInvalid; +} + +SBType +SBType::GetBasicType(lldb::BasicType basic_type) +{ + if (IsValid()) + return SBType (ClangASTContext::GetBasicType (m_opaque_sp->GetASTContext(), basic_type)); + return SBType(); +} + +uint32_t +SBType::GetNumberOfDirectBaseClasses () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumDirectBaseClasses(); + return 0; +} + +uint32_t +SBType::GetNumberOfVirtualBaseClasses () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumVirtualBaseClasses(); + return 0; +} + +uint32_t +SBType::GetNumberOfFields () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumFields(); + return 0; +} + +bool +SBType::GetDescription (SBStream &description, lldb::DescriptionLevel 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) +{ + SBTypeMember sb_type_member; + if (IsValid()) + { + ClangASTType this_type (m_opaque_sp->GetClangASTType ()); + if (this_type.IsValid()) + { + uint32_t bit_offset = 0; + ClangASTType base_class_type (this_type.GetDirectBaseClassAtIndex(idx, &bit_offset)); + if (base_class_type.IsValid()) + { + sb_type_member.reset (new TypeMemberImpl (TypeImplSP(new TypeImpl(base_class_type)), bit_offset)); + } + } + } + return sb_type_member; + +} + +SBTypeMember +SBType::GetVirtualBaseClassAtIndex (uint32_t idx) +{ + SBTypeMember sb_type_member; + if (IsValid()) + { + ClangASTType this_type (m_opaque_sp->GetClangASTType ()); + if (this_type.IsValid()) + { + uint32_t bit_offset = 0; + ClangASTType base_class_type (this_type.GetVirtualBaseClassAtIndex(idx, &bit_offset)); + if (base_class_type.IsValid()) + { + sb_type_member.reset (new TypeMemberImpl (TypeImplSP(new TypeImpl(base_class_type)), bit_offset)); + } + } + } + return sb_type_member; +} + +SBTypeMember +SBType::GetFieldAtIndex (uint32_t idx) +{ + SBTypeMember sb_type_member; + if (IsValid()) + { + ClangASTType this_type (m_opaque_sp->GetClangASTType ()); + if (this_type.IsValid()) + { + uint64_t bit_offset = 0; + uint32_t bitfield_bit_size = 0; + bool is_bitfield = false; + std::string name_sstr; + ClangASTType 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 sb_type_member; +} + +bool +SBType::IsTypeComplete() +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetClangASTType().IsCompleteType(); +} + +const char* +SBType::GetName() +{ + if (!IsValid()) + return ""; + return m_opaque_sp->GetClangASTType().GetConstTypeName().GetCString(); +} + +lldb::TypeClass +SBType::GetTypeClass () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetTypeClass(); + return lldb::eTypeClassInvalid; +} + +uint32_t +SBType::GetNumberOfTemplateArguments () +{ + if (IsValid()) + return m_opaque_sp->GetClangASTType().GetNumTemplateArguments(); + return 0; +} + +lldb::SBType +SBType::GetTemplateArgumentType (uint32_t idx) +{ + if (IsValid()) + { + TemplateArgumentKind kind = eTemplateArgumentKindNull; + ClangASTType template_arg_type = m_opaque_sp->GetClangASTType().GetTemplateArgument (idx, kind); + if (template_arg_type.IsValid()) + return SBType(template_arg_type); + } + return SBType(); +} + + +lldb::TemplateArgumentKind +SBType::GetTemplateArgumentKind (uint32_t idx) +{ + TemplateArgumentKind kind = eTemplateArgumentKindNull; + if (IsValid()) + m_opaque_sp->GetClangASTType().GetTemplateArgument (idx, kind); + return kind; +} + + +SBTypeList::SBTypeList() : + m_opaque_ap(new TypeListImpl()) +{ +} + +SBTypeList::SBTypeList(const SBTypeList& rhs) : + m_opaque_ap(new TypeListImpl()) +{ + for (uint32_t i = 0, rhs_size = const_cast(rhs).GetSize(); i < rhs_size; i++) + Append(const_cast(rhs).GetTypeAtIndex(i)); +} + +bool +SBTypeList::IsValid () +{ + return (m_opaque_ap.get() != NULL); +} + +SBTypeList& +SBTypeList::operator = (const SBTypeList& rhs) +{ + if (this != &rhs) + { + m_opaque_ap.reset (new TypeListImpl()); + for (uint32_t i = 0, rhs_size = const_cast(rhs).GetSize(); i < rhs_size; i++) + Append(const_cast(rhs).GetTypeAtIndex(i)); + } + return *this; +} + +void +SBTypeList::Append (SBType type) +{ + if (type.IsValid()) + m_opaque_ap->Append (type.m_opaque_sp); +} + +SBType +SBTypeList::GetTypeAtIndex(uint32_t index) +{ + if (m_opaque_ap.get()) + return SBType(m_opaque_ap->GetTypeAtIndex(index)); + return SBType(); +} + +uint32_t +SBTypeList::GetSize() +{ + return m_opaque_ap->GetSize(); +} + +SBTypeList::~SBTypeList() +{ +} + +SBTypeMember::SBTypeMember() : + m_opaque_ap() +{ +} + +SBTypeMember::~SBTypeMember() +{ +} + +SBTypeMember::SBTypeMember (const SBTypeMember& rhs) : + m_opaque_ap() +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset(new TypeMemberImpl(rhs.ref())); + } +} + +lldb::SBTypeMember& +SBTypeMember::operator = (const lldb::SBTypeMember& rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset(new TypeMemberImpl(rhs.ref())); + } + return *this; +} + +bool +SBTypeMember::IsValid() const +{ + return m_opaque_ap.get(); +} + +const char * +SBTypeMember::GetName () +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetName().GetCString(); + return NULL; +} + +SBType +SBTypeMember::GetType () +{ + SBType sb_type; + if (m_opaque_ap.get()) + { + sb_type.SetSP (m_opaque_ap->GetTypeImpl()); + } + return sb_type; + +} + +uint64_t +SBTypeMember::GetOffsetInBytes() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetBitOffset() / 8u; + return 0; +} + +uint64_t +SBTypeMember::GetOffsetInBits() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetBitOffset(); + return 0; +} + +bool +SBTypeMember::IsBitfield() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetIsBitfield(); + return false; +} + +uint32_t +SBTypeMember::GetBitfieldSizeInBits() +{ + if (m_opaque_ap.get()) + return m_opaque_ap->GetBitfieldBitSize(); + return 0; +} + + +bool +SBTypeMember::GetDescription (lldb::SBStream &description, lldb::DescriptionLevel description_level) +{ + Stream &strm = description.ref(); + + if (m_opaque_ap.get()) + { + const uint32_t bit_offset = m_opaque_ap->GetBitOffset(); + const uint32_t byte_offset = bit_offset / 8u; + const uint32_t byte_bit_offset = bit_offset % 8u; + const char *name = m_opaque_ap->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_ap->GetTypeImpl()); + if (type_impl_sp) + type_impl_sp->GetDescription(strm, description_level); + + strm.Printf (") %s", name); + if (m_opaque_ap->GetIsBitfield()) + { + const uint32_t bitfield_bit_size = m_opaque_ap->GetBitfieldBitSize(); + strm.Printf (" : %u", bitfield_bit_size); + } + } + else + { + strm.PutCString ("No value"); + } + return true; +} + + +void +SBTypeMember::reset(TypeMemberImpl *type_member_impl) +{ + m_opaque_ap.reset(type_member_impl); +} + +TypeMemberImpl & +SBTypeMember::ref () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new TypeMemberImpl()); + return *m_opaque_ap.get(); +} + +const TypeMemberImpl & +SBTypeMember::ref () const +{ + return *m_opaque_ap.get(); +} diff --git a/source/API/SBTypeCategory.cpp b/source/API/SBTypeCategory.cpp new file mode 100644 index 00000000000..e3978693c81 --- /dev/null +++ b/source/API/SBTypeCategory.cpp @@ -0,0 +1,576 @@ +//===-- SBTypeCategory.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeCategory.h" + +#include "lldb/API/SBTypeFilter.h" +#include "lldb/API/SBTypeFormat.h" +#include "lldb/API/SBTypeSummary.h" +#include "lldb/API/SBTypeSynthetic.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBStream.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 ImplType; + +SBTypeCategory::SBTypeCategory() : +m_opaque_sp() +{ +} + +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) +{ +} + +SBTypeCategory::~SBTypeCategory () +{ +} + +bool +SBTypeCategory::IsValid() const +{ + return (m_opaque_sp.get() != NULL); +} + +bool +SBTypeCategory::GetEnabled () +{ + if (!IsValid()) + return false; + return m_opaque_sp->IsEnabled(); +} + +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() +{ + if (!IsValid()) + return NULL; + return m_opaque_sp->GetName(); +} + +uint32_t +SBTypeCategory::GetNumFormats () +{ + if (!IsDefaultCategory()) + return 0; + + return DataVisualization::ValueFormats::GetCount(); +} + +uint32_t +SBTypeCategory::GetNumSummaries () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetSummaryNavigator()->GetCount() + m_opaque_sp->GetRegexSummaryNavigator()->GetCount(); +} + +uint32_t +SBTypeCategory::GetNumFilters () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetFilterNavigator()->GetCount() + m_opaque_sp->GetRegexFilterNavigator()->GetCount(); +} + +#ifndef LLDB_DISABLE_PYTHON +uint32_t +SBTypeCategory::GetNumSynthetics () +{ + if (!IsValid()) + return 0; + return m_opaque_sp->GetSyntheticNavigator()->GetCount() + m_opaque_sp->GetRegexSyntheticNavigator()->GetCount(); +} +#endif + +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForFilterAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(m_opaque_sp->GetTypeNameSpecifierForFilterAtIndex(index)); +} + +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForFormatAtIndex (uint32_t index) +{ + if (!IsDefaultCategory()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(DataVisualization::ValueFormats::GetTypeNameSpecifierForFormatAtIndex(index)); +} + +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForSummaryAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(m_opaque_sp->GetTypeNameSpecifierForSummaryAtIndex(index)); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SBTypeNameSpecifier +SBTypeCategory::GetTypeNameSpecifierForSyntheticAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeNameSpecifier(); + return SBTypeNameSpecifier(m_opaque_sp->GetTypeNameSpecifierForSyntheticAtIndex(index)); +} +#endif + +SBTypeFilter +SBTypeCategory::GetFilterForType (SBTypeNameSpecifier spec) +{ + if (!IsValid()) + return SBTypeFilter(); + + if (!spec.IsValid()) + return SBTypeFilter(); + + lldb::SyntheticChildrenSP children_sp; + + if (spec.IsRegex()) + m_opaque_sp->GetRegexFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + else + m_opaque_sp->GetFilterNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + + if (!children_sp) + return lldb::SBTypeFilter(); + + TypeFilterImplSP filter_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeFilter(filter_sp); + +} +SBTypeFormat +SBTypeCategory::GetFormatForType (SBTypeNameSpecifier spec) +{ + if (!IsDefaultCategory()) + return SBTypeFormat(); + + if (!spec.IsValid()) + return SBTypeFormat(); + + if (spec.IsRegex()) + return SBTypeFormat(); + + return SBTypeFormat(DataVisualization::ValueFormats::GetFormat(ConstString(spec.GetName()))); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSummary +SBTypeCategory::GetSummaryForType (SBTypeNameSpecifier spec) +{ + if (!IsValid()) + return SBTypeSummary(); + + if (!spec.IsValid()) + return SBTypeSummary(); + + lldb::TypeSummaryImplSP summary_sp; + + if (spec.IsRegex()) + m_opaque_sp->GetRegexSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + else + m_opaque_sp->GetSummaryNavigator()->GetExact(ConstString(spec.GetName()), summary_sp); + + if (!summary_sp) + return lldb::SBTypeSummary(); + + return lldb::SBTypeSummary(summary_sp); +} +#endif // LLDB_DISABLE_PYTHON + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSynthetic +SBTypeCategory::GetSyntheticForType (SBTypeNameSpecifier spec) +{ + if (!IsValid()) + return SBTypeSynthetic(); + + if (!spec.IsValid()) + return SBTypeSynthetic(); + + lldb::SyntheticChildrenSP children_sp; + + if (spec.IsRegex()) + m_opaque_sp->GetRegexSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + else + m_opaque_sp->GetSyntheticNavigator()->GetExact(ConstString(spec.GetName()), children_sp); + + if (!children_sp) + return lldb::SBTypeSynthetic(); + + ScriptedSyntheticChildrenSP synth_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeSynthetic(synth_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +SBTypeFilter +SBTypeCategory::GetFilterAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeFilter(); + lldb::SyntheticChildrenSP children_sp = m_opaque_sp->GetSyntheticAtIndex((index)); + + if (!children_sp.get()) + return lldb::SBTypeFilter(); + + TypeFilterImplSP filter_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeFilter(filter_sp); +} +#endif + +SBTypeFormat +SBTypeCategory::GetFormatAtIndex (uint32_t index) +{ + if (!IsDefaultCategory()) + return SBTypeFormat(); + return SBTypeFormat(DataVisualization::ValueFormats::GetFormatAtIndex((index))); +} + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSummary +SBTypeCategory::GetSummaryAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeSummary(); + return SBTypeSummary(m_opaque_sp->GetSummaryAtIndex((index))); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +SBTypeSynthetic +SBTypeCategory::GetSyntheticAtIndex (uint32_t index) +{ + if (!IsValid()) + return SBTypeSynthetic(); + lldb::SyntheticChildrenSP children_sp = m_opaque_sp->GetSyntheticAtIndex((index)); + + if (!children_sp.get()) + return lldb::SBTypeSynthetic(); + + ScriptedSyntheticChildrenSP synth_sp = std::static_pointer_cast(children_sp); + + return lldb::SBTypeSynthetic(synth_sp); +} +#endif + +bool +SBTypeCategory::AddTypeFormat (SBTypeNameSpecifier type_name, + SBTypeFormat format) +{ + if (!IsDefaultCategory()) + return false; + + if (!type_name.IsValid()) + return false; + + if (!format.IsValid()) + return false; + + if (type_name.IsRegex()) + return false; + + DataVisualization::ValueFormats::Add(ConstString(type_name.GetName()), format.GetSP()); + + return true; +} + +bool +SBTypeCategory::DeleteTypeFormat (SBTypeNameSpecifier type_name) +{ + if (!IsDefaultCategory()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return false; + + return DataVisualization::ValueFormats::Delete(ConstString(type_name.GetName())); +} + +#ifndef LLDB_DISABLE_PYTHON +bool +SBTypeCategory::AddTypeSummary (SBTypeNameSpecifier type_name, + SBTypeSummary 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()) + { + void *name_token = (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->GetCommandInterpreter().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->GetRegexSummaryNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), summary.GetSP()); + else + m_opaque_sp->GetSummaryNavigator()->Add(ConstString(type_name.GetName()), summary.GetSP()); + + return true; +} +#endif + +bool +SBTypeCategory::DeleteTypeSummary (SBTypeNameSpecifier type_name) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return m_opaque_sp->GetRegexSummaryNavigator()->Delete(ConstString(type_name.GetName())); + else + return m_opaque_sp->GetSummaryNavigator()->Delete(ConstString(type_name.GetName())); +} + +bool +SBTypeCategory::AddTypeFilter (SBTypeNameSpecifier type_name, + SBTypeFilter filter) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (!filter.IsValid()) + return false; + + if (type_name.IsRegex()) + m_opaque_sp->GetRegexFilterNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), filter.GetSP()); + else + m_opaque_sp->GetFilterNavigator()->Add(ConstString(type_name.GetName()), filter.GetSP()); + + return true; +} + +bool +SBTypeCategory::DeleteTypeFilter (SBTypeNameSpecifier type_name) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return m_opaque_sp->GetRegexFilterNavigator()->Delete(ConstString(type_name.GetName())); + else + return m_opaque_sp->GetFilterNavigator()->Delete(ConstString(type_name.GetName())); +} + +#ifndef LLDB_DISABLE_PYTHON +bool +SBTypeCategory::AddTypeSynthetic (SBTypeNameSpecifier type_name, + SBTypeSynthetic 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()) + { + void *name_token = (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->GetCommandInterpreter().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->GetRegexSyntheticNavigator()->Add(lldb::RegularExpressionSP(new RegularExpression(type_name.GetName())), synth.GetSP()); + else + m_opaque_sp->GetSyntheticNavigator()->Add(ConstString(type_name.GetName()), synth.GetSP()); + + return true; +} + +bool +SBTypeCategory::DeleteTypeSynthetic (SBTypeNameSpecifier type_name) +{ + if (!IsValid()) + return false; + + if (!type_name.IsValid()) + return false; + + if (type_name.IsRegex()) + return m_opaque_sp->GetRegexSyntheticNavigator()->Delete(ConstString(type_name.GetName())); + else + return m_opaque_sp->GetSyntheticNavigator()->Delete(ConstString(type_name.GetName())); +} +#endif // LLDB_DISABLE_PYTHON + +bool +SBTypeCategory::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!IsValid()) + return false; + description.Printf("Category name: %s\n",GetName()); + return true; +} + +lldb::SBTypeCategory & +SBTypeCategory::operator = (const lldb::SBTypeCategory &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeCategory::operator == (lldb::SBTypeCategory &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return m_opaque_sp.get() == rhs.m_opaque_sp.get(); + +} + +bool +SBTypeCategory::operator != (lldb::SBTypeCategory &rhs) +{ + if (IsValid() == false) + 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); +} + diff --git a/source/API/SBTypeFilter.cpp b/source/API/SBTypeFilter.cpp new file mode 100644 index 00000000000..605e92de699 --- /dev/null +++ b/source/API/SBTypeFilter.cpp @@ -0,0 +1,199 @@ +//===-- SBTypeFilter.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeFilter.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +SBTypeFilter::SBTypeFilter() : +m_opaque_sp() +{ +} + +SBTypeFilter::SBTypeFilter (uint32_t options) +: m_opaque_sp(TypeFilterImplSP(new TypeFilterImpl(options))) +{ +} + +SBTypeFilter::SBTypeFilter (const lldb::SBTypeFilter &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeFilter::~SBTypeFilter () +{ +} + +bool +SBTypeFilter::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +uint32_t +SBTypeFilter::GetOptions() +{ + if (IsValid()) + return m_opaque_sp->GetOptions(); + return 0; +} + +void +SBTypeFilter::SetOptions (uint32_t value) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->SetOptions(value); +} + +bool +SBTypeFilter::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!IsValid()) + return false; + else { + description.Printf("%s\n", + m_opaque_sp->GetDescription().c_str()); + return true; + } +} + +void +SBTypeFilter::Clear() +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->Clear(); +} + +uint32_t +SBTypeFilter::GetNumberOfExpressionPaths() +{ + if (IsValid()) + return m_opaque_sp->GetCount(); + return 0; +} + +const char* +SBTypeFilter::GetExpressionPathAtIndex (uint32_t i) +{ + if (IsValid()) + { + const char* item = m_opaque_sp->GetExpressionPathAtIndex(i); + if (item && *item == '.') + item++; + return item; + } + return NULL; +} + +bool +SBTypeFilter::ReplaceExpressionPathAtIndex (uint32_t i, const char* item) +{ + if (CopyOnWrite_Impl()) + return m_opaque_sp->SetExpressionPathAtIndex(i, item); + else + return false; +} + +void +SBTypeFilter::AppendExpressionPath (const char* item) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->AddExpressionPath(item); +} + +lldb::SBTypeFilter & +SBTypeFilter::operator = (const lldb::SBTypeFilter &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeFilter::operator == (lldb::SBTypeFilter &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeFilter::IsEqualTo (lldb::SBTypeFilter &rhs) +{ + if (IsValid() == false) + 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) +{ + if (IsValid() == false) + 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; +} diff --git a/source/API/SBTypeFormat.cpp b/source/API/SBTypeFormat.cpp new file mode 100644 index 00000000000..34ab404a206 --- /dev/null +++ b/source/API/SBTypeFormat.cpp @@ -0,0 +1,155 @@ +//===-- SBTypeFormat.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeFormat.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +SBTypeFormat::SBTypeFormat() : +m_opaque_sp() +{ +} + +SBTypeFormat::SBTypeFormat (lldb::Format format, + uint32_t options) +: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl(format,options))) +{ +} + +SBTypeFormat::SBTypeFormat (const lldb::SBTypeFormat &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeFormat::~SBTypeFormat () +{ +} + +bool +SBTypeFormat::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +lldb::Format +SBTypeFormat::GetFormat () +{ + if (IsValid()) + return m_opaque_sp->GetFormat(); + return lldb::eFormatInvalid; +} + +uint32_t +SBTypeFormat::GetOptions() +{ + if (IsValid()) + return m_opaque_sp->GetOptions(); + return 0; +} + +void +SBTypeFormat::SetFormat (lldb::Format fmt) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->SetFormat(fmt); +} + +void +SBTypeFormat::SetOptions (uint32_t value) +{ + if (CopyOnWrite_Impl()) + m_opaque_sp->SetOptions(value); +} + +bool +SBTypeFormat::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel 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) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeFormat::operator == (lldb::SBTypeFormat &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeFormat::IsEqualTo (lldb::SBTypeFormat &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (GetFormat() == rhs.GetFormat()) + return GetOptions() == rhs.GetOptions(); + else + return false; +} + +bool +SBTypeFormat::operator != (lldb::SBTypeFormat &rhs) +{ + if (IsValid() == false) + 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() +{ + if (!IsValid()) + return false; + if (m_opaque_sp.unique()) + return true; + + SetSP(TypeFormatImplSP(new TypeFormatImpl(GetFormat(),GetOptions()))); + return true; +} diff --git a/source/API/SBTypeNameSpecifier.cpp b/source/API/SBTypeNameSpecifier.cpp new file mode 100644 index 00000000000..d417499ecbd --- /dev/null +++ b/source/API/SBTypeNameSpecifier.cpp @@ -0,0 +1,150 @@ +//===-- SBTypeNameSpecifier.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeNameSpecifier.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() +{ +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (const char* name, + bool is_regex) : +m_opaque_sp(new TypeNameSpecifierImpl(name, is_regex)) +{ + if (name == NULL || (*name) == 0) + m_opaque_sp.reset(); +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (SBType type) : +m_opaque_sp() +{ + if (type.IsValid()) + m_opaque_sp = TypeNameSpecifierImplSP(new TypeNameSpecifierImpl(type.m_opaque_sp->GetClangASTType())); +} + +SBTypeNameSpecifier::SBTypeNameSpecifier (const lldb::SBTypeNameSpecifier &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{} + +SBTypeNameSpecifier::~SBTypeNameSpecifier () +{ +} + +bool +SBTypeNameSpecifier::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +const char* +SBTypeNameSpecifier::GetName () +{ + if (!IsValid()) + return NULL; + + return m_opaque_sp->GetName(); +} + +SBType +SBTypeNameSpecifier::GetType () +{ + if (!IsValid()) + return SBType(); + lldb_private::ClangASTType c_type = m_opaque_sp->GetClangASTType(); + if (c_type.IsValid()) + return SBType(c_type); + return SBType(); +} + +bool +SBTypeNameSpecifier::IsRegex () +{ + if (!IsValid()) + return false; + + return m_opaque_sp->IsRegex(); +} + +bool +SBTypeNameSpecifier::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel 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) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeNameSpecifier::operator == (lldb::SBTypeNameSpecifier &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeNameSpecifier::IsEqualTo (lldb::SBTypeNameSpecifier &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (IsRegex() != rhs.IsRegex()) + return false; + if (GetName() == NULL || rhs.GetName() == NULL) + return false; + + return (strcmp(GetName(), rhs.GetName()) == 0); +} + +bool +SBTypeNameSpecifier::operator != (lldb::SBTypeNameSpecifier &rhs) +{ + if (IsValid() == false) + 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) +{ +} diff --git a/source/API/SBTypeSummary.cpp b/source/API/SBTypeSummary.cpp new file mode 100644 index 00000000000..aaa09c289cb --- /dev/null +++ b/source/API/SBTypeSummary.cpp @@ -0,0 +1,335 @@ +//===-- SBTypeSummary.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeSummary.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef LLDB_DISABLE_PYTHON + +SBTypeSummary::SBTypeSummary() : +m_opaque_sp() +{ +} + +SBTypeSummary +SBTypeSummary::CreateWithSummaryString (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary(TypeSummaryImplSP(new StringSummaryFormat(options, data))); +} + +SBTypeSummary +SBTypeSummary::CreateWithFunctionName (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary(TypeSummaryImplSP(new ScriptSummaryFormat(options, data))); +} + +SBTypeSummary +SBTypeSummary::CreateWithScriptCode (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSummary(); + + return SBTypeSummary(TypeSummaryImplSP(new ScriptSummaryFormat(options, "", data))); +} + +SBTypeSummary::SBTypeSummary (const lldb::SBTypeSummary &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeSummary::~SBTypeSummary () +{ +} + +bool +SBTypeSummary::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +bool +SBTypeSummary::IsFunctionCode() +{ + if (!IsValid()) + return false; + if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* script_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + const char* ftext = script_summary_ptr->GetPythonScript(); + return (ftext && *ftext != 0); + } + return false; +} + +bool +SBTypeSummary::IsFunctionName() +{ + if (!IsValid()) + return false; + if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* script_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + const char* ftext = script_summary_ptr->GetPythonScript(); + return (!ftext || *ftext == 0); + } + return false; +} + +bool +SBTypeSummary::IsSummaryString() +{ + if (!IsValid()) + return false; + + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + return false; + + return !m_opaque_sp->IsScripted(); +} + +const char* +SBTypeSummary::GetData () +{ + if (!IsValid()) + return NULL; + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + return NULL; + if (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* script_summary_ptr = (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 + { + StringSummaryFormat* string_summary_ptr = (StringSummaryFormat*)m_opaque_sp.get(); + return string_summary_ptr->GetSummaryString(); + } +} + +uint32_t +SBTypeSummary::GetOptions () +{ + if (!IsValid()) + return lldb::eTypeOptionNone; + return m_opaque_sp->GetOptions(); +} + +void +SBTypeSummary::SetOptions (uint32_t value) +{ + if (!CopyOnWrite_Impl()) + return; + m_opaque_sp->SetOptions(value); +} + +void +SBTypeSummary::SetSummaryString (const char* data) +{ + if (!IsValid()) + return; + if (m_opaque_sp->IsScripted() || (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback)) + ChangeSummaryType(false); + ((StringSummaryFormat*)m_opaque_sp.get())->SetSummaryString(data); +} + +void +SBTypeSummary::SetFunctionName (const char* data) +{ + if (!IsValid()) + return; + if (!m_opaque_sp->IsScripted()) + ChangeSummaryType(true); + ((ScriptSummaryFormat*)m_opaque_sp.get())->SetFunctionName(data); +} + +void +SBTypeSummary::SetFunctionCode (const char* data) +{ + if (!IsValid()) + return; + if (!m_opaque_sp->IsScripted()) + ChangeSummaryType(true); + ((ScriptSummaryFormat*)m_opaque_sp.get())->SetPythonScript(data); +} + +bool +SBTypeSummary::GetDescription (lldb::SBStream &description, + lldb::DescriptionLevel description_level) +{ + if (!CopyOnWrite_Impl()) + return false; + else { + description.Printf("%s\n", + m_opaque_sp->GetDescription().c_str()); + return true; + } +} + +lldb::SBTypeSummary & +SBTypeSummary::operator = (const lldb::SBTypeSummary &rhs) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeSummary::operator == (lldb::SBTypeSummary &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeSummary::IsEqualTo (lldb::SBTypeSummary &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + + if (m_opaque_sp->GetType() != rhs.m_opaque_sp->GetType()) + return false; + + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + { + lldb_private::CXXFunctionSummaryFormat *self_cxx = (lldb_private::CXXFunctionSummaryFormat*)m_opaque_sp.get(); + lldb_private::CXXFunctionSummaryFormat *other_cxx = (lldb_private::CXXFunctionSummaryFormat*)rhs.m_opaque_sp.get(); + return (self_cxx->m_impl == other_cxx->m_impl); + } + + if (m_opaque_sp->IsScripted() != rhs.m_opaque_sp->IsScripted()) + return false; + + if (IsFunctionCode() != rhs.IsFunctionCode()) + return false; + + if (IsSummaryString() != rhs.IsSummaryString()) + return false; + + if (IsFunctionName() != rhs.IsFunctionName()) + return false; + + if ( GetData() == NULL || rhs.GetData() == NULL || strcmp(GetData(), rhs.GetData()) ) + return false; + + return GetOptions() == rhs.GetOptions(); + +} + +bool +SBTypeSummary::operator != (lldb::SBTypeSummary &rhs) +{ + if (IsValid() == false) + 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 (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback) + { + CXXFunctionSummaryFormat* current_summary_ptr = (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 (m_opaque_sp->IsScripted()) + { + ScriptSummaryFormat* current_summary_ptr = (ScriptSummaryFormat*)m_opaque_sp.get(); + new_sp = TypeSummaryImplSP(new ScriptSummaryFormat(GetOptions(), + current_summary_ptr->GetFunctionName(), + current_summary_ptr->GetPythonScript())); + } + else { + StringSummaryFormat* current_summary_ptr = (StringSummaryFormat*)m_opaque_sp.get(); + new_sp = TypeSummaryImplSP(new StringSummaryFormat(GetOptions(), + current_summary_ptr->GetSummaryString())); + } + + SetSP(new_sp); + + return true; +} + +bool +SBTypeSummary::ChangeSummaryType (bool want_script) +{ + if (!IsValid()) + return false; + + TypeSummaryImplSP new_sp; + + if (want_script == m_opaque_sp->IsScripted()) + { + if (m_opaque_sp->GetType() == lldb_private::TypeSummaryImpl::eTypeCallback && !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; +} + +#endif // LLDB_DISABLE_PYTHON diff --git a/source/API/SBTypeSynthetic.cpp b/source/API/SBTypeSynthetic.cpp new file mode 100644 index 00000000000..681ed6c032a --- /dev/null +++ b/source/API/SBTypeSynthetic.cpp @@ -0,0 +1,209 @@ +//===-- SBTypeSynthetic.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBTypeSynthetic.h" + +#include "lldb/API/SBStream.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +using namespace lldb; +using namespace lldb_private; + +#ifndef LLDB_DISABLE_PYTHON + +SBTypeSynthetic::SBTypeSynthetic() : +m_opaque_sp() +{ +} + +SBTypeSynthetic +SBTypeSynthetic::CreateWithClassName (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSynthetic(); + return SBTypeSynthetic(ScriptedSyntheticChildrenSP(new ScriptedSyntheticChildren(options, data, ""))); +} + +SBTypeSynthetic +SBTypeSynthetic::CreateWithScriptCode (const char* data, uint32_t options) +{ + if (!data || data[0] == 0) + return SBTypeSynthetic(); + return SBTypeSynthetic(ScriptedSyntheticChildrenSP(new ScriptedSyntheticChildren(options, "", data))); +} + +SBTypeSynthetic::SBTypeSynthetic (const lldb::SBTypeSynthetic &rhs) : +m_opaque_sp(rhs.m_opaque_sp) +{ +} + +SBTypeSynthetic::~SBTypeSynthetic () +{ +} + +bool +SBTypeSynthetic::IsValid() const +{ + return m_opaque_sp.get() != NULL; +} + +bool +SBTypeSynthetic::IsClassCode() +{ + if (!IsValid()) + return false; + const char* code = m_opaque_sp->GetPythonCode(); + return (code && *code); +} + +bool +SBTypeSynthetic::IsClassName() +{ + if (!IsValid()) + return false; + return !IsClassCode(); +} + +const char* +SBTypeSynthetic::GetData () +{ + if (!IsValid()) + return NULL; + if (IsClassCode()) + return m_opaque_sp->GetPythonCode(); + else + return m_opaque_sp->GetPythonClassName(); +} + +void +SBTypeSynthetic::SetClassName (const char* data) +{ + if (IsValid() && data && *data) + m_opaque_sp->SetPythonClassName(data); +} + +void +SBTypeSynthetic::SetClassCode (const char* data) +{ + if (IsValid() && data && *data) + m_opaque_sp->SetPythonCode(data); +} + +uint32_t +SBTypeSynthetic::GetOptions () +{ + if (!IsValid()) + return lldb::eTypeOptionNone; + return m_opaque_sp->GetOptions(); +} + +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) +{ + 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) +{ + if (this != &rhs) + { + m_opaque_sp = rhs.m_opaque_sp; + } + return *this; +} + +bool +SBTypeSynthetic::operator == (lldb::SBTypeSynthetic &rhs) +{ + if (IsValid() == false) + return !rhs.IsValid(); + return m_opaque_sp == rhs.m_opaque_sp; +} + +bool +SBTypeSynthetic::IsEqualTo (lldb::SBTypeSynthetic &rhs) +{ + if (IsValid() == false) + 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) +{ + if (IsValid() == false) + 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; +} + +#endif // LLDB_DISABLE_PYTHON diff --git a/source/API/SBValue.cpp b/source/API/SBValue.cpp new file mode 100644 index 00000000000..aa9b23ac7c6 --- /dev/null +++ b/source/API/SBValue.cpp @@ -0,0 +1,1719 @@ +//===-- SBValue.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBValue.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/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.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/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" + +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 = NULL) : + m_valobj_sp(in_valobj_sp), + m_use_dynamic(use_dynamic), + m_use_synthetic(use_synthetic), + m_name (name) + { + if (!m_name.IsEmpty() && m_valobj_sp) + 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 () + { + return m_valobj_sp.get() != NULL; + } + + lldb::ValueObjectSP + GetRootSP () + { + return m_valobj_sp; + } + + lldb::ValueObjectSP + GetSP (Process::StopLocker &stop_locker, Mutex::Locker &api_locker, Error &error) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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) + api_locker.Lock(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. + if (log) + log->Printf ("SBValue(%p)::GetSP() => error: process is running", value_sp.get()); + error.SetErrorString ("process must be stopped."); + return ValueObjectSP(); + } + + if (value_sp->GetDynamicValue(m_use_dynamic)) + value_sp = value_sp->GetDynamicValue(m_use_dynamic); + if (value_sp->GetSyntheticValue(m_use_synthetic)) + value_sp = value_sp->GetSyntheticValue(m_use_synthetic); + 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_api_locker, m_lock_error); + } + + Error & + GetError() + { + return m_lock_error; + } + +private: + Process::StopLocker m_stop_locker; + Mutex::Locker m_api_locker; + Error m_lock_error; + +}; + +SBValue::SBValue () : + m_opaque_sp () +{ +} + +SBValue::SBValue (const lldb::ValueObjectSP &value_sp) +{ + SetSP(value_sp); +} + +SBValue::SBValue(const SBValue &rhs) +{ + SetSP(rhs.m_opaque_sp); +} + +SBValue & +SBValue::operator = (const SBValue &rhs) +{ + if (this != &rhs) + { + SetSP(rhs.m_opaque_sp); + } + return *this; +} + +SBValue::~SBValue() +{ +} + +bool +SBValue::IsValid () +{ + // 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() != NULL && m_opaque_sp->GetRootSP().get() != NULL; +} + +void +SBValue::Clear() +{ + m_opaque_sp.reset(); +} + +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 sb_error; +} + +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() +{ + const char *name = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + name = value_sp->GetName().GetCString(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (name) + log->Printf ("SBValue(%p)::GetName () => \"%s\"", value_sp.get(), name); + else + log->Printf ("SBValue(%p)::GetName () => NULL", value_sp.get()); + } + + return name; +} + +const char * +SBValue::GetTypeName () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *name = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + name = value_sp->GetQualifiedTypeName().GetCString(); + } + + if (log) + { + if (name) + log->Printf ("SBValue(%p)::GetTypeName () => \"%s\"", value_sp.get(), name); + else + log->Printf ("SBValue(%p)::GetTypeName () => NULL", value_sp.get()); + } + + return name; +} + +size_t +SBValue::GetByteSize () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + size_t result = 0; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + result = value_sp->GetByteSize(); + } + + if (log) + log->Printf ("SBValue(%p)::GetByteSize () => %" PRIu64, value_sp.get(), (uint64_t)result); + + return result; +} + +bool +SBValue::IsInScope () +{ + bool result = false; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + result = value_sp->IsInScope (); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::IsInScope () => %i", value_sp.get(), result); + + return result; +} + +const char * +SBValue::GetValue () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetValueAsCString (); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetValue() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetValue() => NULL", value_sp.get()); + } + + return cstr; +} + +ValueType +SBValue::GetValueType () +{ + ValueType result = eValueTypeInvalid; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + result = value_sp->GetValueType(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + switch (result) + { + case eValueTypeInvalid: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeInvalid", value_sp.get()); break; + case eValueTypeVariableGlobal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableGlobal", value_sp.get()); break; + case eValueTypeVariableStatic: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableStatic", value_sp.get()); break; + case eValueTypeVariableArgument:log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableArgument", value_sp.get()); break; + case eValueTypeVariableLocal: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeVariableLocal", value_sp.get()); break; + case eValueTypeRegister: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegister", value_sp.get()); break; + case eValueTypeRegisterSet: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeRegisterSet", value_sp.get()); break; + case eValueTypeConstResult: log->Printf ("SBValue(%p)::GetValueType () => eValueTypeConstResult", value_sp.get()); break; + } + } + return result; +} + +const char * +SBValue::GetObjectDescription () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetObjectDescription (); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetObjectDescription() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetObjectDescription() => NULL", value_sp.get()); + } + return cstr; +} + +SBType +SBValue::GetType() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + SBType sb_type; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + TypeImplSP type_sp; + if (value_sp) + { + type_sp.reset (new TypeImpl(value_sp->GetClangType())); + sb_type.SetSP(type_sp); + } + if (log) + { + if (type_sp) + log->Printf ("SBValue(%p)::GetType => SBType(%p)", value_sp.get(), type_sp.get()); + else + log->Printf ("SBValue(%p)::GetType => NULL", value_sp.get()); + } + return sb_type; +} + +bool +SBValue::GetValueDidChange () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool result = false; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + result = value_sp->GetValueDidChange (); + } + if (log) + log->Printf ("SBValue(%p)::GetValueDidChange() => %i", value_sp.get(), result); + + return result; +} + +#ifndef LLDB_DISABLE_PYTHON +const char * +SBValue::GetSummary () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetSummaryAsCString(); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetSummary() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetSummary() => NULL", value_sp.get()); + } + return cstr; +} +#endif // LLDB_DISABLE_PYTHON + +const char * +SBValue::GetLocation () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + const char *cstr = NULL; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + cstr = value_sp->GetLocationAsCString(); + } + if (log) + { + if (cstr) + log->Printf ("SBValue(%p)::GetLocation() => \"%s\"", value_sp.get(), cstr); + else + log->Printf ("SBValue(%p)::GetLocation() => NULL", value_sp.get()); + } + return cstr; +} + +// Deprecated - use the one that takes an lldb::SBError +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) +{ + bool success = false; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (value_sp) + { + success = value_sp->SetValueFromCString (value_str,error.ref()); + } + else + error.SetErrorStringWithFormat ("Could not get value: %s", locker.GetError().AsCString()); + + if (log) + log->Printf ("SBValue(%p)::SetValueFromCString(\"%s\") => %i", value_sp.get(), value_str, success); + + return success; +} + +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 format; +} + +#ifndef LLDB_DISABLE_PYTHON +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 summary; +} +#endif // LLDB_DISABLE_PYTHON + +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(synthetic_sp); + filter.SetSP(filter_sp); + } + } + } + return filter; +} + +#ifndef LLDB_DISABLE_PYTHON +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(children_sp); + synthetic.SetSP(synth_sp); + } + } + } + return synthetic; +} +#endif + +lldb::SBValue +SBValue::CreateChildAtOffset (const char *name, uint32_t offset, SBType 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->GetClangASTType(), true),GetPreferDynamicValue(),GetPreferSyntheticValue(), name); + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateChildAtOffset => \"%s\"", + value_sp.get(), + new_value_sp->GetName().AsCString()); + else + log->Printf ("SBValue(%p)::CreateChildAtOffset => NULL", + value_sp.get()); + } + return sb_value; +} + +lldb::SBValue +SBValue::Cast (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->GetClangASTType()),GetPreferDynamicValue(),GetPreferSyntheticValue()); + return sb_value; +} + +lldb::SBValue +SBValue::CreateValueFromExpression (const char *name, const char* expression) +{ + SBExpressionOptions options; + options.ref().SetKeepInMemory(true); + return CreateValueFromExpression (name, expression, options); +} + +lldb::SBValue +SBValue::CreateValueFromExpression (const char *name, const char *expression, SBExpressionOptions &options) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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()); + Target* target = exe_ctx.GetTargetPtr(); + if (target) + { + options.ref().SetKeepInMemory(true); + target->EvaluateExpression (expression, + exe_ctx.GetFramePtr(), + new_value_sp, + options.ref()); + if (new_value_sp) + { + new_value_sp->SetName(ConstString(name)); + sb_value.SetSP(new_value_sp); + } + } + } + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateValueFromExpression(name=\"%s\", expression=\"%s\") => SBValue (%p)", + value_sp.get(), + name, + expression, + new_value_sp.get()); + else + log->Printf ("SBValue(%p)::CreateValueFromExpression(name=\"%s\", expression=\"%s\") => NULL", + value_sp.get(), + name, + expression); + } + return sb_value; +} + +lldb::SBValue +SBValue::CreateValueFromAddress(const char* name, lldb::addr_t address, SBType 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) + { + ClangASTType pointee_ast_type(type_impl_sp->GetClangASTType().GetPointerType ()); + if (pointee_ast_type) + { + lldb::DataBufferSP buffer(new lldb_private::DataBufferHeap(&address,sizeof(lldb::addr_t))); + + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); + ValueObjectSP ptr_result_valobj_sp(ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + pointee_ast_type, + ConstString(name), + buffer, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize())); + + if (ptr_result_valobj_sp) + { + ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); + Error err; + new_value_sp = ptr_result_valobj_sp->Dereference(err); + if (new_value_sp) + new_value_sp->SetName(ConstString(name)); + } + sb_value.SetSP(new_value_sp); + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateValueFromAddress => \"%s\"", value_sp.get(), new_value_sp->GetName().AsCString()); + else + log->Printf ("SBValue(%p)::CreateValueFromAddress => NULL", value_sp.get()); + } + return sb_value; +} + +lldb::SBValue +SBValue::CreateValueFromData (const char* name, SBData data, SBType type) +{ + lldb::SBValue sb_value; + lldb::ValueObjectSP new_value_sp; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + ExecutionContext exe_ctx (value_sp->GetExecutionContextRef()); + + new_value_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + type.m_opaque_sp->GetClangASTType(), + ConstString(name), + *data.m_opaque_sp, + LLDB_INVALID_ADDRESS); + new_value_sp->SetAddressTypeOfChildren(eAddressTypeLoad); + sb_value.SetSP(new_value_sp); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (new_value_sp) + log->Printf ("SBValue(%p)::CreateValueFromData => \"%s\"", value_sp.get(), new_value_sp->GetName().AsCString()); + else + log->Printf ("SBValue(%p)::CreateValueFromData => NULL", value_sp.get()); + } + return sb_value; +} + +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 GetChildAtIndex (idx, use_dynamic, can_create_synthetic); +} + +SBValue +SBValue::GetChildAtIndex (uint32_t idx, lldb::DynamicValueType use_dynamic, bool can_create_synthetic) +{ + lldb::ValueObjectSP child_sp; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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) + { + if (value_sp->IsPointerType()) + { + child_sp = value_sp->GetSyntheticArrayMemberFromPointer(idx, can_create); + } + else if (value_sp->IsArrayType()) + { + child_sp = value_sp->GetSyntheticArrayMemberFromArray(idx, can_create); + } + } + } + + SBValue sb_value; + sb_value.SetSP (child_sp, use_dynamic, GetPreferSyntheticValue()); + if (log) + log->Printf ("SBValue(%p)::GetChildAtIndex (%u) => SBValue(%p)", value_sp.get(), idx, value_sp.get()); + + return sb_value; +} + +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)); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (idx == UINT32_MAX) + log->Printf ("SBValue(%p)::GetIndexOfChildWithName (name=\"%s\") => NOT FOUND", value_sp.get(), name); + else + log->Printf ("SBValue(%p)::GetIndexOfChildWithName (name=\"%s\") => %u", value_sp.get(), name, idx); + } + return idx; +} + +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 GetChildMemberWithName (name, use_dynamic_value); +} + +SBValue +SBValue::GetChildMemberWithName (const char *name, lldb::DynamicValueType use_dynamic_value) +{ + lldb::ValueObjectSP child_sp; + const ConstString str_name (name); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + 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()); + + if (log) + log->Printf ("SBValue(%p)::GetChildMemberWithName (name=\"%s\") => SBValue(%p)", value_sp.get(), name, value_sp.get()); + + return sb_value; +} + +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 value_sb; +} + +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 value_sb; +} + +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 value_sb; +} + +lldb::DynamicValueType +SBValue::GetPreferDynamicValue () +{ + if (!IsValid()) + return eNoDynamicValues; + return m_opaque_sp->GetUseDynamic(); +} + +void +SBValue::SetPreferDynamicValue (lldb::DynamicValueType use_dynamic) +{ + if (IsValid()) + return m_opaque_sp->SetUseDynamic (use_dynamic); +} + +bool +SBValue::GetPreferSyntheticValue () +{ + if (!IsValid()) + return false; + return m_opaque_sp->GetUseSynthetic(); +} + +void +SBValue::SetPreferSyntheticValue (bool use_synthetic) +{ + if (IsValid()) + return m_opaque_sp->SetUseSynthetic (use_synthetic); +} + +bool +SBValue::IsDynamic() +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->IsDynamic(); + return false; +} + +bool +SBValue::IsSynthetic () +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->IsSynthetic(); + return false; +} + +lldb::SBValue +SBValue::GetValueForExpressionPath(const char* expr_path) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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()); + + if (log) + log->Printf ("SBValue(%p)::GetValueForExpressionPath (expr_path=\"%s\") => SBValue(%p)", value_sp.get(), expr_path, value_sp.get()); + + return sb_value; +} + +int64_t +SBValue::GetValueAsSigned(SBError& error, int64_t fail_value) +{ + error.Clear(); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.SLongLong (fail_value); + else + error.SetErrorString ("could not resolve value"); + } + else + error.SetErrorStringWithFormat ("could not get SBValue: %s", locker.GetError().AsCString()); + + return fail_value; +} + +uint64_t +SBValue::GetValueAsUnsigned(SBError& error, uint64_t fail_value) +{ + error.Clear(); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.ULongLong(fail_value); + else + error.SetErrorString("could not resolve value"); + } + else + error.SetErrorStringWithFormat ("could not get SBValue: %s", locker.GetError().AsCString()); + + return fail_value; +} + +int64_t +SBValue::GetValueAsSigned(int64_t fail_value) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.SLongLong(fail_value); + } + return fail_value; +} + +uint64_t +SBValue::GetValueAsUnsigned(uint64_t fail_value) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Scalar scalar; + if (value_sp->ResolveValue (scalar)) + return scalar.ULongLong(fail_value); + } + return fail_value; +} + +bool +SBValue::MightHaveChildren () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + bool has_children = false; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + has_children = value_sp->MightHaveChildren(); + + if (log) + log->Printf ("SBValue(%p)::MightHaveChildren() => %i", value_sp.get(), has_children); + return has_children; +} + +uint32_t +SBValue::GetNumChildren () +{ + uint32_t num_children = 0; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + num_children = value_sp->GetNumChildren(); + + if (log) + log->Printf ("SBValue(%p)::GetNumChildren () => %u", value_sp.get(), num_children); + + return num_children; +} + + +SBValue +SBValue::Dereference () +{ + SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Error error; + sb_value = value_sp->Dereference (error); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::Dereference () => SBValue(%p)", value_sp.get(), value_sp.get()); + + return sb_value; +} + +bool +SBValue::TypeIsPointerType () +{ + bool is_ptr_type = false; + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + is_ptr_type = value_sp->IsPointerType(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::TypeIsPointerType () => %i", value_sp.get(), is_ptr_type); + + + return is_ptr_type; +} + +void * +SBValue::GetOpaqueType() +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + return value_sp->GetClangType().GetOpaqueQualType(); + return NULL; +} + +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); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (target_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetTarget () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetTarget () => %p", m_opaque_sp.get(), target_sp.get()); + } + return sb_target; +} + +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); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (process_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetProcess () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetProcess () => %p", m_opaque_sp.get(), process_sp.get()); + } + return sb_process; +} + +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); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (thread_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetThread () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetThread () => %p", m_opaque_sp.get(), thread_sp.get()); + } + return sb_thread; +} + +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); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + { + if (frame_sp.get() == NULL) + log->Printf ("SBValue(%p)::GetFrame () => NULL", m_opaque_sp.get()); + else + log->Printf ("SBValue(%p)::GetFrame () => %p", m_opaque_sp.get(), frame_sp.get()); + } + return sb_frame; +} + + +lldb::ValueObjectSP +SBValue::GetSP (ValueLocker &locker) const +{ + if (!m_opaque_sp || !m_opaque_sp->IsValid()) + return ValueObjectSP(); + return locker.GetLockedSP(*m_opaque_sp.get()); +} + +lldb::ValueObjectSP +SBValue::GetSP () const +{ + ValueLocker locker; + return 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) +{ + 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) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + value_sp->GetExpressionPath (description.ref(), qualify_cxx_base_classes); + return true; + } + return false; +} + +bool +SBValue::GetDescription (SBStream &description) +{ + Stream &strm = description.ref(); + + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + ValueObject::DumpValueObject (strm, value_sp.get()); + } + else + strm.PutCString ("No value"); + + return true; +} + +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) +{ + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + value_sp->SetFormat(format); +} + +lldb::SBValue +SBValue::AddressOf() +{ + SBValue sb_value; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + Error error; + sb_value.SetSP(value_sp->AddressOf (error),GetPreferDynamicValue(), GetPreferSyntheticValue()); + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::AddressOf () => SBValue(%p)", value_sp.get(), value_sp.get()); + + return sb_value; +} + +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; + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::GetLoadAddress () => (%" PRIu64 ")", value_sp.get(), value); + + return value; +} + +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()); + } + } + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::GetAddress () => (%s,%" PRIu64 ")", value_sp.get(), + (addr.GetSection() ? addr.GetSection()->GetName().GetCString() : "NULL"), + addr.GetOffset()); + return SBAddress(new Address(addr)); +} + +lldb::SBData +SBValue::GetPointeeData (uint32_t item_idx, + uint32_t item_count) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + 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; + } + } + if (log) + log->Printf ("SBValue(%p)::GetPointeeData (%d, %d) => SBData(%p)", + value_sp.get(), + item_idx, + item_count, + sb_data.get()); + + return sb_data; +} + +lldb::SBData +SBValue::GetData () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + lldb::SBData sb_data; + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + if (value_sp) + { + DataExtractorSP data_sp(new DataExtractor()); + value_sp->GetData(*data_sp); + if (data_sp->GetByteSize() > 0) + *sb_data = data_sp; + } + if (log) + log->Printf ("SBValue(%p)::GetData () => SBData(%p)", + value_sp.get(), + sb_data.get()); + + return sb_data; +} + +bool +SBValue::SetData (lldb::SBData &data, SBError &error) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + ValueLocker locker; + lldb::ValueObjectSP value_sp(GetSP(locker)); + bool ret = true; + + if (value_sp) + { + DataExtractor *data_extractor = data.get(); + + if (!data_extractor) + { + if (log) + log->Printf ("SBValue(%p)::SetData() => error: no data to set", value_sp.get()); + + error.SetErrorString("No data to set"); + ret = false; + } + else + { + Error 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; + } + + if (log) + log->Printf ("SBValue(%p)::SetData (%p) => %s", + value_sp.get(), + data.get(), + ret ? "true" : "false"); + return ret; +} + +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 decl_sb; +} + +lldb::SBWatchpoint +SBValue::Watch (bool resolve_location, bool read, bool write, SBError &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 sb_watchpoint; + + // If the value is not in scope, don't try and watch and invalid value + if (!IsInScope()) + return sb_watchpoint; + + addr_t addr = GetLoadAddress(); + if (addr == LLDB_INVALID_ADDRESS) + return sb_watchpoint; + size_t byte_size = GetByteSize(); + if (byte_size == 0) + return sb_watchpoint; + + uint32_t watch_type = 0; + if (read) + watch_type |= LLDB_WATCH_TYPE_READ; + if (write) + watch_type |= LLDB_WATCH_TYPE_WRITE; + + Error rc; + ClangASTType type (value_sp->GetClangType()); + 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) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::Watch() => error getting SBValue: %s", value_sp.get(), locker.GetError().AsCString()); + + error.SetErrorStringWithFormat("could not get SBValue: %s", locker.GetError().AsCString()); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBValue(%p)::Watch() => error getting SBValue: no target", value_sp.get()); + error.SetErrorString("could not set watchpoint, a target is required"); + } + + return 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) +{ + SBError error; + return Watch(resolve_location, read, write, error); +} + +lldb::SBWatchpoint +SBValue::WatchPointee (bool resolve_location, bool read, bool write, SBError &error) +{ + SBWatchpoint sb_watchpoint; + if (IsInScope() && GetType().IsPointerType()) + sb_watchpoint = Dereference().Watch (resolve_location, read, write, error); + return sb_watchpoint; +} diff --git a/source/API/SBValueList.cpp b/source/API/SBValueList.cpp new file mode 100644 index 00000000000..46866eb3742 --- /dev/null +++ b/source/API/SBValueList.cpp @@ -0,0 +1,275 @@ +//===-- SBValueList.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/API/SBValueList.h" +#include "lldb/API/SBValue.h" +#include "lldb/API/SBStream.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ValueObjectList.h" + +#include + +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(); + } + +private: + std::vector m_values; +}; + +SBValueList::SBValueList () : + m_opaque_ap () +{ +} + +SBValueList::SBValueList (const SBValueList &rhs) : + m_opaque_ap () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (rhs.IsValid()) + m_opaque_ap.reset (new ValueListImpl (*rhs)); + + if (log) + { + log->Printf ("SBValueList::SBValueList (rhs.ap=%p) => this.ap = %p", + (rhs.IsValid() ? rhs.m_opaque_ap.get() : NULL), + m_opaque_ap.get()); + } +} + +SBValueList::SBValueList (const ValueListImpl *lldb_object_ptr) : + m_opaque_ap () +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (lldb_object_ptr) + m_opaque_ap.reset (new ValueListImpl (*lldb_object_ptr)); + + if (log) + { + log->Printf ("SBValueList::SBValueList (lldb_object_ptr=%p) => this.ap = %p", + lldb_object_ptr, + m_opaque_ap.get()); + } +} + +SBValueList::~SBValueList () +{ +} + +bool +SBValueList::IsValid () const +{ + return (m_opaque_ap.get() != NULL); +} + +void +SBValueList::Clear() +{ + m_opaque_ap.reset(); +} + +const SBValueList & +SBValueList::operator = (const SBValueList &rhs) +{ + if (this != &rhs) + { + if (rhs.IsValid()) + m_opaque_ap.reset (new ValueListImpl (*rhs)); + else + m_opaque_ap.reset (); + } + return *this; +} + +ValueListImpl * +SBValueList::operator->() +{ + return m_opaque_ap.get(); +} + +ValueListImpl & +SBValueList::operator*() +{ + return *m_opaque_ap; +} + +const ValueListImpl * +SBValueList::operator->() const +{ + return m_opaque_ap.get(); +} + +const ValueListImpl & +SBValueList::operator*() const +{ + return *m_opaque_ap; +} + +void +SBValueList::Append (const SBValue &val_obj) +{ + CreateIfNeeded (); + m_opaque_ap->Append (val_obj); +} + +void +SBValueList::Append (lldb::ValueObjectSP& val_obj_sp) +{ + if (val_obj_sp) + { + CreateIfNeeded (); + m_opaque_ap->Append (SBValue(val_obj_sp)); + } +} + +void +SBValueList::Append (const lldb::SBValueList& value_list) +{ + if (value_list.IsValid()) + { + CreateIfNeeded (); + m_opaque_ap->Append (*value_list); + } +} + + +SBValue +SBValueList::GetValueAtIndex (uint32_t idx) const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + //if (log) + // log->Printf ("SBValueList::GetValueAtIndex (uint32_t idx) idx = %d", idx); + + SBValue sb_value; + if (m_opaque_ap.get()) + sb_value = m_opaque_ap->GetValueAtIndex (idx); + + if (log) + { + SBStream sstr; + sb_value.GetDescription (sstr); + log->Printf ("SBValueList::GetValueAtIndex (this.ap=%p, idx=%d) => SBValue (this.sp = %p, '%s')", + m_opaque_ap.get(), idx, sb_value.GetSP().get(), sstr.GetData()); + } + + return sb_value; +} + +uint32_t +SBValueList::GetSize () const +{ + Log *log(GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + //if (log) + // log->Printf ("SBValueList::GetSize ()"); + + uint32_t size = 0; + if (m_opaque_ap.get()) + size = m_opaque_ap->GetSize(); + + if (log) + log->Printf ("SBValueList::GetSize (this.ap=%p) => %d", m_opaque_ap.get(), size); + + return size; +} + +void +SBValueList::CreateIfNeeded () +{ + if (m_opaque_ap.get() == NULL) + m_opaque_ap.reset (new ValueListImpl()); +} + + +SBValue +SBValueList::FindValueObjectByUID (lldb::user_id_t uid) +{ + SBValue sb_value; + if (m_opaque_ap.get()) + sb_value = m_opaque_ap->FindValueByUID(uid); + return sb_value; +} + +void * +SBValueList::opaque_ptr () +{ + return m_opaque_ap.get(); +} + +ValueListImpl & +SBValueList::ref () +{ + CreateIfNeeded(); + return *m_opaque_ap.get(); +} + + diff --git a/source/API/SBWatchpoint.cpp b/source/API/SBWatchpoint.cpp new file mode 100644 index 00000000000..194695c31d5 --- /dev/null +++ b/source/API/SBWatchpoint.cpp @@ -0,0 +1,298 @@ +//===-- SBWatchpoint.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/API/SBWatchpoint.h" +#include "lldb/API/SBDefines.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBStream.h" + +#include "lldb/lldb-types.h" +#include "lldb/lldb-defines.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + + +SBWatchpoint::SBWatchpoint () : + m_opaque_sp () +{ +} + +SBWatchpoint::SBWatchpoint (const lldb::WatchpointSP &wp_sp) : + m_opaque_sp (wp_sp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + if (log) + { + SBStream sstr; + GetDescription (sstr, lldb::eDescriptionLevelBrief); + log->Printf ("SBWatchpoint::SBWatchpoint (const lldb::WatchpointSP &wp_sp" + "=%p) => this.sp = %p (%s)", wp_sp.get(), m_opaque_sp.get(), sstr.GetData()); + } +} + +SBWatchpoint::SBWatchpoint(const SBWatchpoint &rhs) : + m_opaque_sp (rhs.m_opaque_sp) +{ +} + +const SBWatchpoint & +SBWatchpoint::operator = (const SBWatchpoint &rhs) +{ + if (this != &rhs) + m_opaque_sp = rhs.m_opaque_sp; + return *this; +} + + +SBWatchpoint::~SBWatchpoint () +{ +} + +watch_id_t +SBWatchpoint::GetID () +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + + watch_id_t watch_id = LLDB_INVALID_WATCH_ID; + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + watch_id = watchpoint_sp->GetID(); + + if (log) + { + if (watch_id == LLDB_INVALID_WATCH_ID) + log->Printf ("SBWatchpoint(%p)::GetID () => LLDB_INVALID_WATCH_ID", watchpoint_sp.get()); + else + log->Printf ("SBWatchpoint(%p)::GetID () => %u", watchpoint_sp.get(), watch_id); + } + + return watch_id; +} + +bool +SBWatchpoint::IsValid() const +{ + return (bool) m_opaque_sp; +} + +SBError +SBWatchpoint::GetError () +{ + SBError sb_error; + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + sb_error.SetError(watchpoint_sp->GetError()); + } + return sb_error; +} + +int32_t +SBWatchpoint::GetHardwareIndex () +{ + int32_t hw_index = -1; + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + hw_index = watchpoint_sp->GetHardwareIndex(); + } + + return hw_index; +} + +addr_t +SBWatchpoint::GetWatchAddress () +{ + addr_t ret_addr = LLDB_INVALID_ADDRESS; + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + ret_addr = watchpoint_sp->GetLoadAddress(); + } + + return ret_addr; +} + +size_t +SBWatchpoint::GetWatchSize () +{ + size_t watch_size = 0; + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watch_size = watchpoint_sp->GetByteSize(); + } + + return watch_size; +} + +void +SBWatchpoint::SetEnabled (bool enabled) +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->GetTarget().DisableWatchpointByID(watchpoint_sp->GetID()); + } +} + +bool +SBWatchpoint::IsEnabled () +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->IsEnabled(); + } + else + return false; +} + +uint32_t +SBWatchpoint::GetHitCount () +{ + uint32_t count = 0; + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + count = watchpoint_sp->GetHitCount(); + } + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_API)); + if (log) + log->Printf ("SBWatchpoint(%p)::GetHitCount () => %u", watchpoint_sp.get(), count); + + return count; +} + +uint32_t +SBWatchpoint::GetIgnoreCount () +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->GetIgnoreCount(); + } + else + return 0; +} + +void +SBWatchpoint::SetIgnoreCount (uint32_t n) +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->SetIgnoreCount (n); + } +} + +const char * +SBWatchpoint::GetCondition () +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + return watchpoint_sp->GetConditionText (); + } + return NULL; +} + +void +SBWatchpoint::SetCondition (const char *condition) +{ + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->SetCondition (condition); + } +} + +bool +SBWatchpoint::GetDescription (SBStream &description, DescriptionLevel level) +{ + Stream &strm = description.ref(); + + lldb::WatchpointSP watchpoint_sp(GetSP()); + if (watchpoint_sp) + { + Mutex::Locker api_locker (watchpoint_sp->GetTarget().GetAPIMutex()); + watchpoint_sp->GetDescription (&strm, level); + strm.EOL(); + } + else + strm.PutCString ("No value"); + + return true; +} + +void +SBWatchpoint::Clear () +{ + m_opaque_sp.reset(); +} + +lldb::WatchpointSP +SBWatchpoint::GetSP () const +{ + return m_opaque_sp; +} + +void +SBWatchpoint::SetSP (const lldb::WatchpointSP &sp) +{ + m_opaque_sp = sp; +} + +bool +SBWatchpoint::EventIsWatchpointEvent (const lldb::SBEvent &event) +{ + return Watchpoint::WatchpointEventData::GetEventDataFromEvent(event.get()) != NULL; + +} + +WatchpointEventType +SBWatchpoint::GetWatchpointEventTypeFromEvent (const SBEvent& event) +{ + if (event.IsValid()) + return Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent (event.GetSP()); + return eWatchpointEventTypeInvalidType; +} + +SBWatchpoint +SBWatchpoint::GetWatchpointFromEvent (const lldb::SBEvent& event) +{ + SBWatchpoint sb_watchpoint; + if (event.IsValid()) + sb_watchpoint.m_opaque_sp = Watchpoint::WatchpointEventData::GetWatchpointFromEvent (event.GetSP()); + return sb_watchpoint; +} diff --git a/source/Breakpoint/Breakpoint.cpp b/source/Breakpoint/Breakpoint.cpp new file mode 100644 index 00000000000..9bc43814b48 --- /dev/null +++ b/source/Breakpoint/Breakpoint.cpp @@ -0,0 +1,794 @@ +//===-- Breakpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Breakpoint/BreakpointResolver.h" +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/lldb-private-log.h" +#include "llvm/Support/Casting.h" + +using namespace lldb; +using namespace lldb_private; +using namespace llvm; + +const ConstString & +Breakpoint::GetEventIdentifier () +{ + static ConstString g_identifier("event-identifier.breakpoint.changed"); + return g_identifier; +} + +//---------------------------------------------------------------------- +// Breakpoint constructor +//---------------------------------------------------------------------- +Breakpoint::Breakpoint(Target &target, SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp) : + m_being_created(true), + m_target (target), + m_filter_sp (filter_sp), + m_resolver_sp (resolver_sp), + m_options (), + m_locations (*this) +{ + m_being_created = false; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Breakpoint::~Breakpoint() +{ +} + +bool +Breakpoint::IsInternal () const +{ + return LLDB_BREAK_ID_IS_INTERNAL(m_bid); +} + + + +Target& +Breakpoint::GetTarget () +{ + return m_target; +} + +const Target& +Breakpoint::GetTarget () const +{ + return m_target; +} + +BreakpointLocationSP +Breakpoint::AddLocation (const Address &addr, bool *new_location) +{ + return m_locations.AddLocation (addr, 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); +} + +// 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.IsEnabled()) + return; + + m_options.SetEnabled(enable); + if (enable) + m_locations.ResolveAllBreakpointSites(); + else + m_locations.ClearAllBreakpointSites(); + + SendBreakpointChangedEvent (enable ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); + +} + +bool +Breakpoint::IsEnabled () +{ + return m_options.IsEnabled(); +} + +void +Breakpoint::SetIgnoreCount (uint32_t n) +{ + if (m_options.GetIgnoreCount() == n) + return; + + m_options.SetIgnoreCount(n); + SendBreakpointChangedEvent (eBreakpointEventTypeIgnoreChanged); +} + +void +Breakpoint::DecrementIgnoreCount () +{ + uint32_t ignore = m_options.GetIgnoreCount(); + if (ignore != 0) + m_options.SetIgnoreCount(ignore - 1); +} + +uint32_t +Breakpoint::GetIgnoreCount () const +{ + return m_options.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_locations.GetHitCount(); +} + +bool +Breakpoint::IsOneShot () const +{ + return m_options.IsOneShot(); +} + +void +Breakpoint::SetOneShot (bool one_shot) +{ + m_options.SetOneShot (one_shot); +} + +void +Breakpoint::SetThreadID (lldb::tid_t thread_id) +{ + if (m_options.GetThreadSpec()->GetTID() == thread_id) + return; + + m_options.GetThreadSpec()->SetTID(thread_id); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t +Breakpoint::GetThreadID () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return LLDB_INVALID_THREAD_ID; + else + return m_options.GetThreadSpecNoCreate()->GetTID(); +} + +void +Breakpoint::SetThreadIndex (uint32_t index) +{ + if (m_options.GetThreadSpec()->GetIndex() == index) + return; + + m_options.GetThreadSpec()->SetIndex(index); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +uint32_t +Breakpoint::GetThreadIndex() const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return 0; + else + return m_options.GetThreadSpecNoCreate()->GetIndex(); +} + +void +Breakpoint::SetThreadName (const char *thread_name) +{ + if (m_options.GetThreadSpec()->GetName() != NULL + && ::strcmp (m_options.GetThreadSpec()->GetName(), thread_name) == 0) + return; + + m_options.GetThreadSpec()->SetName (thread_name); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +Breakpoint::GetThreadName () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return NULL; + else + return m_options.GetThreadSpecNoCreate()->GetName(); +} + +void +Breakpoint::SetQueueName (const char *queue_name) +{ + if (m_options.GetThreadSpec()->GetQueueName() != NULL + && ::strcmp (m_options.GetThreadSpec()->GetQueueName(), queue_name) == 0) + return; + + m_options.GetThreadSpec()->SetQueueName (queue_name); + SendBreakpointChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +Breakpoint::GetQueueName () const +{ + if (m_options.GetThreadSpecNoCreate() == NULL) + return NULL; + else + return m_options.GetThreadSpecNoCreate()->GetQueueName(); +} + +void +Breakpoint::SetCondition (const char *condition) +{ + m_options.SetCondition (condition); + SendBreakpointChangedEvent (eBreakpointEventTypeConditionChanged); +} + +const char * +Breakpoint::GetConditionText () const +{ + return m_options.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.SetCallback(callback, BatonSP (new Baton(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.SetCallback(callback, callback_baton_sp, is_synchronous); +} + +void +Breakpoint::ClearCallback () +{ + m_options.ClearCallback (); +} + +bool +Breakpoint::InvokeCallback (StoppointCallbackContext *context, break_id_t bp_loc_id) +{ + return m_options.InvokeCallback (context, GetID(), bp_loc_id); +} + +BreakpointOptions * +Breakpoint::GetOptions () +{ + return &m_options; +} + +void +Breakpoint::ResolveBreakpoint () +{ + if (m_resolver_sp) + m_resolver_sp->ResolveBreakpoint(*m_filter_sp); +} + +void +Breakpoint::ResolveBreakpointInModules (ModuleList &module_list) +{ + if (m_resolver_sp) + 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) +{ + Mutex::Locker modules_mutex(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. + + const size_t num_locs = m_locations.GetSize(); + size_t num_modules = module_list.GetSize(); + for (size_t i = 0; i < num_modules; i++) + { + bool seen = false; + ModuleSP module_sp (module_list.GetModuleAtIndexUnlocked (i)); + if (!m_filter_sp->ModulePasses (module_sp)) + continue; + + for (size_t loc_idx = 0; loc_idx < num_locs; loc_idx++) + { + BreakpointLocationSP break_loc = m_locations.GetByIndex(loc_idx); + if (!break_loc->IsEnabled()) + continue; + SectionSP section_sp (break_loc->GetAddress().GetSection()); + if (!section_sp || section_sp->GetModule() == module_sp) + { + if (!seen) + seen = true; + + if (!break_loc->ResolveBreakpointSite()) + { + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + if (log) + log->Printf ("Warning: could not set breakpoint site for breakpoint location %d of breakpoint %d.\n", + break_loc->GetID(), GetID()); + } + } + } + + if (!seen) + new_modules.AppendIfNeeded (module_sp); + + } + + if (new_modules.GetSize() > 0) + { + // If this is not an internal breakpoint, set up to record the new locations, then dispatch + // an event with the new locations. + if (!IsInternal()) + { + BreakpointEventData *new_locations_event = new BreakpointEventData (eBreakpointEventTypeLocationsAdded, + shared_from_this()); + + m_locations.StartRecordingNewLocations(new_locations_event->GetBreakpointLocationCollection()); + + ResolveBreakpointInModules(new_modules); + + m_locations.StopRecordingNewLocations(); + if (new_locations_event->GetBreakpointLocationCollection().GetSize() != 0) + { + SendBreakpointChangedEvent (new_locations_event); + } + else + delete new_locations_event; + } + else + 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 = NULL; + + 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); + } +} + +void +Breakpoint::ModuleReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) +{ + ModuleList temp_list; + temp_list.Append (new_module_sp); + ModulesChanged (temp_list, true); + + // TO DO: For now I'm just adding locations for the new module and removing the + // breakpoint locations that were in the old module. + // We should really go find the ones that are in the new module & if we can determine that they are "equivalent" + // carry over the options from the old location to the new. + + temp_list.Clear(); + temp_list.Append (old_module_sp); + ModulesChanged (temp_list, false, true); +} + +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(); +} + +size_t +Breakpoint::GetNumLocations() const +{ + return m_locations.GetSize(); +} + +void +Breakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level, bool show_locations) +{ + assert (s != NULL); + + if (!m_kind_description.empty()) + { + if (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, (uint64_t)num_resolved_locations); + } + 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 (level == lldb::eDescriptionLevelFull) + { + 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) + { + // If there is one location only, we'll just print that location information. But don't do this if + // show locations is true, then that will be handled below. + if (show_locations == false) + { + GetLocationAtIndex(0)->GetDescription(s, level); + } + else + { + s->Printf ("%zd locations.", num_locations); + } + } + else + { + s->Printf ("%zd locations.", 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 (const 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(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); +} + +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 == NULL) + 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 () +{ +} + +const ConstString & +Breakpoint::BreakpointEventData::GetFlavorString () +{ + static ConstString g_flavor ("Breakpoint::BreakpointEventData"); + return g_flavor; +} + +const 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 (event->GetData()); + } + return NULL; +} + +BreakpointEventType +Breakpoint::BreakpointEventData::GetBreakpointEventTypeFromEvent (const EventSP &event_sp) +{ + const BreakpointEventData *data = GetEventDataFromEvent (event_sp.get()); + + if (data == NULL) + 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/source/Breakpoint/BreakpointID.cpp b/source/Breakpoint/BreakpointID.cpp new file mode 100644 index 00000000000..9a59e29d007 --- /dev/null +++ b/source/Breakpoint/BreakpointID.cpp @@ -0,0 +1,123 @@ +//===-- BreakpointID.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/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 () +{ +} + +const char *BreakpointID::g_range_specifiers[] = { "-", "to", "To", "TO", NULL }; + +// 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 (const char *str) +{ + int specifier_count = 0; + for (int i = 0; g_range_specifiers[i] != NULL; ++i) + ++specifier_count; + + for (int i = 0; i < specifier_count; ++i) + { + if (strcmp (g_range_specifiers[i], str) == 0) + return true; + } + + return false; +} + +bool +BreakpointID::IsValidIDExpression (const char *str) +{ + break_id_t bp_id; + break_id_t loc_id; + BreakpointID::ParseCanonicalReference (str, &bp_id, &loc_id); + + if (bp_id == LLDB_INVALID_BREAK_ID) + return false; + else + return true; +} + +void +BreakpointID::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + if (level == eDescriptionLevelVerbose) + s->Printf("%p BreakpointID:", this); + + if (m_break_id == LLDB_INVALID_BREAK_ID) + s->PutCString (""); + 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 (""); + else if (loc_id == LLDB_INVALID_BREAK_ID) + s->Printf("%i", bp_id); + else + s->Printf("%i.%i", bp_id, loc_id); +} + +bool +BreakpointID::ParseCanonicalReference (const char *input, break_id_t *break_id_ptr, break_id_t *break_loc_id_ptr) +{ + *break_id_ptr = LLDB_INVALID_BREAK_ID; + *break_loc_id_ptr = LLDB_INVALID_BREAK_ID; + + if (input == NULL || *input == '\0') + return false; + + const char *format = "%i%n.%i%n"; + int chars_consumed_1 = 0; + int chars_consumed_2 = 0; + int n_items_parsed = ::sscanf (input, + format, + break_id_ptr, // %i parse the breakpoint ID + &chars_consumed_1, // %n gets the number of characters parsed so far + break_loc_id_ptr, // %i parse the breakpoint location ID + &chars_consumed_2); // %n gets the number of characters parsed so far + + if ((n_items_parsed == 1 && input[chars_consumed_1] == '\0') || + (n_items_parsed == 2 && input[chars_consumed_2] == '\0')) + return true; + + // Badly formatted canonical reference. + *break_id_ptr = LLDB_INVALID_BREAK_ID; + *break_loc_id_ptr = LLDB_INVALID_BREAK_ID; + return false; +} + diff --git a/source/Breakpoint/BreakpointIDList.cpp b/source/Breakpoint/BreakpointIDList.cpp new file mode 100644 index 00000000000..24101b1442f --- /dev/null +++ b/source/Breakpoint/BreakpointIDList.cpp @@ -0,0 +1,397 @@ +//===-- BreakpointIDList.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointIDList.h" + +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Target/Target.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 () +{ +} + +size_t +BreakpointIDList::GetSize() +{ + return m_breakpoint_ids.size(); +} + +BreakpointID & +BreakpointIDList::GetBreakpointIDAtIndex (size_t index) +{ + if (index < m_breakpoint_ids.size()) + return m_breakpoint_ids[index]; + else + return 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) +{ + BreakpointID temp_bp_id; + break_id_t bp_id; + break_id_t loc_id; + + bool success = BreakpointID::ParseCanonicalReference (bp_id_str, &bp_id, &loc_id); + + if (success) + { + temp_bp_id.SetID (bp_id, loc_id); + m_breakpoint_ids.push_back (temp_bp_id); + } + + return success; +} + +bool +BreakpointIDList::FindBreakpointID (BreakpointID &bp_id, size_t *position) +{ + 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) +{ + BreakpointID temp_bp_id; + break_id_t bp_id; + break_id_t loc_id; + + if (BreakpointID::ParseCanonicalReference (bp_id_str, &bp_id, &loc_id)) + { + temp_bp_id.SetID (bp_id, loc_id); + return FindBreakpointID (temp_bp_id, position); + } + else + return false; +} + +void +BreakpointIDList::InsertStringArray (const char **string_array, size_t array_size, CommandReturnObject &result) +{ + if (string_array == NULL) + return; + + for (uint32_t i = 0; i < array_size; ++i) + { + break_id_t bp_id; + break_id_t loc_id; + + if (BreakpointID::ParseCanonicalReference (string_array[i], &bp_id, &loc_id)) + { + if (bp_id != LLDB_INVALID_BREAK_ID) + { + BreakpointID temp_bp_id(bp_id, loc_id); + m_breakpoint_ids.push_back (temp_bp_id); + } + else + { + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", string_array[i]); + result.SetStatus (eReturnStatusFailed); + return; + } + } + } + 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, CommandReturnObject &result, + Args &new_args) +{ + std::string range_start; + const char *range_end; + const char *current_arg; + const size_t num_old_args = old_args.GetArgumentCount(); + + for (size_t i = 0; i < num_old_args; ++i) + { + bool is_range = false; + current_arg = old_args.GetArgumentAtIndex (i); + + size_t range_start_len = 0; + size_t range_end_pos = 0; + if (BreakpointIDList::StringContainsIDRangeExpression (current_arg, &range_start_len, &range_end_pos)) + { + is_range = true; + range_start.assign (current_arg, range_start_len); + range_end = current_arg + range_end_pos; + } + else if ((i + 2 < num_old_args) + && BreakpointID::IsRangeIdentifier (old_args.GetArgumentAtIndex (i+1)) + && BreakpointID::IsValidIDExpression (current_arg) + && BreakpointID::IsValidIDExpression (old_args.GetArgumentAtIndex (i+2))) + { + range_start.assign (current_arg); + range_end = old_args.GetArgumentAtIndex (i+2); + is_range = true; + i = i+2; + } + else + { + // See if user has specified id.* + std::string tmp_str = old_args.GetArgumentAtIndex (i); + size_t pos = tmp_str.find ('.'); + if (pos != std::string::npos) + { + std::string bp_id_str = tmp_str.substr (0, pos); + if (BreakpointID::IsValidIDExpression (bp_id_str.c_str()) + && tmp_str[pos+1] == '*' + && tmp_str.length() == (pos + 2)) + { + break_id_t bp_id; + break_id_t bp_loc_id; + + BreakpointID::ParseCanonicalReference (bp_id_str.c_str(), &bp_id, &bp_loc_id); + BreakpointSP breakpoint_sp = target->GetBreakpointByID (bp_id); + if (! breakpoint_sp) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%d' is not a valid breakpoint ID.\n", bp_id); + 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, bp_loc->GetID()); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + + } + } + + if (is_range) + { + break_id_t start_bp_id; + break_id_t end_bp_id; + break_id_t start_loc_id; + break_id_t end_loc_id; + + BreakpointID::ParseCanonicalReference (range_start.c_str(), &start_bp_id, &start_loc_id); + BreakpointID::ParseCanonicalReference (range_end, &end_bp_id, &end_loc_id); + + if ((start_bp_id == LLDB_INVALID_BREAK_ID) + || (! target->GetBreakpointByID (start_bp_id))) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_start.c_str()); + result.SetStatus (eReturnStatusFailed); + return; + } + + if ((end_bp_id == LLDB_INVALID_BREAK_ID) + || (! target->GetBreakpointByID (end_bp_id))) + { + new_args.Clear(); + result.AppendErrorWithFormat ("'%s' is not a valid breakpoint ID.\n", range_end); + result.SetStatus (eReturnStatusFailed); + return; + } + + + 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.GetData()); + } + } + } + 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.GetData()); + } + } + } + else + { + StreamString canonical_id_str; + BreakpointID::GetCanonicalReference (&canonical_id_str, cur_bp_id, LLDB_INVALID_BREAK_ID); + new_args.AppendArgument (canonical_id_str.GetData()); + } + } + } + else // else is_range was false + { + new_args.AppendArgument (current_arg); + } + } + + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return; +} + +bool +BreakpointIDList::StringContainsIDRangeExpression (const char *in_string, + size_t *range_start_len, + size_t *range_end_pos) +{ + bool is_range_expression = false; + std::string arg_str = in_string; + std::string::size_type idx; + std::string::size_type start_pos = 0; + + *range_start_len = 0; + *range_end_pos = 0; + + int specifiers_size = 0; + for (int i = 0; BreakpointID::g_range_specifiers[i] != NULL; ++i) + ++specifiers_size; + + for (int i = 0; i < specifiers_size && !is_range_expression; ++i) + { + const char *specifier_str = BreakpointID::g_range_specifiers[i]; + size_t len = strlen (specifier_str); + idx = arg_str.find (BreakpointID::g_range_specifiers[i]); + if (idx != std::string::npos) + { + *range_start_len = idx - start_pos; + std::string start_str = arg_str.substr (start_pos, *range_start_len); + if (idx + len < arg_str.length()) + { + *range_end_pos = idx + len; + std::string end_str = arg_str.substr (*range_end_pos); + if (BreakpointID::IsValidIDExpression (start_str.c_str()) + && BreakpointID::IsValidIDExpression (end_str.c_str())) + { + is_range_expression = true; + //*range_start = start_str; + //*range_end = end_str; + } + } + } + } + + if (!is_range_expression) + { + *range_start_len = 0; + *range_end_pos = 0; + } + + return is_range_expression; +} diff --git a/source/Breakpoint/BreakpointList.cpp b/source/Breakpoint/BreakpointList.cpp new file mode 100644 index 00000000000..5926663af7b --- /dev/null +++ b/source/Breakpoint/BreakpointList.cpp @@ -0,0 +1,243 @@ +//===-- BreakpointList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointList::BreakpointList (bool is_internal) : + m_mutex (Mutex::eMutexTypeRecursive), + m_breakpoints(), + m_next_break_id (0), + m_is_internal (is_internal) +{ +} + +BreakpointList::~BreakpointList() +{ +} + + +break_id_t +BreakpointList::Add (BreakpointSP &bp_sp, bool notify) +{ + Mutex::Locker locker(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) + { + if (bp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + bp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeAdded, bp_sp)); + } + return bp_sp->GetID(); +} + +bool +BreakpointList::Remove (break_id_t break_id, bool notify) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); // Predicate + if (pos != m_breakpoints.end()) + { + BreakpointSP bp_sp (*pos); + m_breakpoints.erase(pos); + if (notify) + { + if (bp_sp->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + bp_sp->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeRemoved, bp_sp)); + } + return true; + } + return false; +} + +void +BreakpointList::SetEnabledAll (bool enabled) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->SetEnabled (enabled); +} + + +void +BreakpointList::RemoveAll (bool notify) +{ + Mutex::Locker locker(m_mutex); + ClearAllBreakpointSites (); + + if (notify) + { + bp_collection::iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + { + if ((*pos)->GetTarget().EventTypeHasListeners(Target::eBroadcastBitBreakpointChanged)) + { + (*pos)->GetTarget().BroadcastEvent (Target::eBroadcastBitBreakpointChanged, + new Breakpoint::BreakpointEventData (eBreakpointEventTypeRemoved, + *pos)); + } + } + } + m_breakpoints.erase (m_breakpoints.begin(), m_breakpoints.end()); +} + +class BreakpointIDMatches +{ +public: + BreakpointIDMatches (break_id_t break_id) : + m_break_id(break_id) + { + } + + bool operator() (const BreakpointSP &bp) const + { + return m_break_id == bp->GetID(); + } + +private: + const break_id_t m_break_id; +}; + +BreakpointList::bp_collection::iterator +BreakpointList::GetBreakpointIDIterator (break_id_t break_id) +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(break_id)); // Predicate +} + +BreakpointList::bp_collection::const_iterator +BreakpointList::GetBreakpointIDConstIterator (break_id_t break_id) const +{ + return std::find_if(m_breakpoints.begin(), m_breakpoints.end(), // Search full range + BreakpointIDMatches(break_id)); // Predicate +} + +BreakpointSP +BreakpointList::FindBreakpointByID (break_id_t break_id) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator pos = GetBreakpointIDIterator(break_id); + if (pos != m_breakpoints.end()) + stop_sp = *pos; + + return stop_sp; +} + +const BreakpointSP +BreakpointList::FindBreakpointByID (break_id_t break_id) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator pos = GetBreakpointIDConstIterator(break_id); + if (pos != m_breakpoints.end()) + stop_sp = *pos; + + return stop_sp; +} + +void +BreakpointList::Dump (Stream *s) const +{ + Mutex::Locker locker(m_mutex); + s->Printf("%p: ", this); + s->Indent(); + s->Printf("BreakpointList with %u Breakpoints:\n", (uint32_t)m_breakpoints.size()); + s->IndentMore(); + bp_collection::const_iterator pos; + bp_collection::const_iterator end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->Dump(s); + s->IndentLess(); +} + + +BreakpointSP +BreakpointList::GetBreakpointAtIndex (size_t i) +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + size_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = *pos; + } + return stop_sp; +} + +const BreakpointSP +BreakpointList::GetBreakpointAtIndex (size_t i) const +{ + Mutex::Locker locker(m_mutex); + BreakpointSP stop_sp; + bp_collection::const_iterator end = m_breakpoints.end(); + bp_collection::const_iterator pos; + size_t curr_i = 0; + for (pos = m_breakpoints.begin(), curr_i = 0; pos != end; ++pos, ++curr_i) + { + if (curr_i == i) + stop_sp = *pos; + } + return stop_sp; +} + +void +BreakpointList::UpdateBreakpoints (ModuleList& module_list, bool added) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ModulesChanged (module_list, added); + +} + +void +BreakpointList::UpdateBreakpointsWhenModuleIsReplaced (ModuleSP old_module_sp, ModuleSP new_module_sp) +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ModuleReplaced (old_module_sp, new_module_sp); + +} + +void +BreakpointList::ClearAllBreakpointSites () +{ + Mutex::Locker locker(m_mutex); + bp_collection::iterator end = m_breakpoints.end(); + bp_collection::iterator pos; + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos)->ClearAllBreakpointSites (); + +} + +void +BreakpointList::GetListMutex (Mutex::Locker &locker) +{ + return locker.Lock (m_mutex); +} diff --git a/source/Breakpoint/BreakpointLocation.cpp b/source/Breakpoint/BreakpointLocation.cpp new file mode 100644 index 00000000000..1ec726dd52b --- /dev/null +++ b/source/Breakpoint/BreakpointLocation.cpp @@ -0,0 +1,677 @@ +//===-- BreakpointLocation.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointID.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.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 +) : + StoppointLocation (loc_id, addr.GetOpcodeLoadAddress(&owner.GetTarget()), hardware), + m_being_created(true), + m_address (addr), + m_owner (owner), + m_options_ap (), + m_bp_site_sp (), + m_condition_mutex () +{ + SetThreadID (tid); + m_being_created = false; +} + +BreakpointLocation::~BreakpointLocation() +{ + ClearBreakpointSite(); +} + +lldb::addr_t +BreakpointLocation::GetLoadAddress () const +{ + return m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()); +} + +Address & +BreakpointLocation::GetAddress () +{ + return m_address; +} + +Breakpoint & +BreakpointLocation::GetBreakpoint () +{ + return m_owner; +} + +bool +BreakpointLocation::IsEnabled () const +{ + if (!m_owner.IsEnabled()) + return false; + else if (m_options_ap.get() != NULL) + return m_options_ap->IsEnabled(); + else + return true; +} + +void +BreakpointLocation::SetEnabled (bool enabled) +{ + GetLocationOptions()->SetEnabled(enabled); + if (enabled) + { + ResolveBreakpointSite(); + } + else + { + ClearBreakpointSite(); + } + SendBreakpointLocationChangedEvent (enabled ? eBreakpointEventTypeEnabled : eBreakpointEventTypeDisabled); +} + +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_ap.get() != NULL) + m_options_ap->SetThreadID (thread_id); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +lldb::tid_t +BreakpointLocation::GetThreadID () +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->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_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetIndex(index); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); + +} + +uint32_t +BreakpointLocation::GetThreadIndex() const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetIndex(); + else + return 0; +} + +void +BreakpointLocation::SetThreadName (const char *thread_name) +{ + if (thread_name != NULL) + 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_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetName(thread_name); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +BreakpointLocation::GetThreadName () const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetName(); + else + return NULL; +} + +void +BreakpointLocation::SetQueueName (const char *queue_name) +{ + if (queue_name != NULL) + 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_ap.get() != NULL) + m_options_ap->GetThreadSpec()->SetQueueName(queue_name); + } + SendBreakpointLocationChangedEvent (eBreakpointEventTypeThreadChanged); +} + +const char * +BreakpointLocation::GetQueueName () const +{ + if (GetOptionsNoCreate()->GetThreadSpecNoCreate()) + return GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetQueueName(); + else + return NULL; +} + +bool +BreakpointLocation::InvokeCallback (StoppointCallbackContext *context) +{ + if (m_options_ap.get() != NULL && m_options_ap->HasCallback()) + return m_options_ap->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, BatonSP (new Baton(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 GetOptionsNoCreate()->GetConditionText(hash); +} + +bool +BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error) +{ + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + + Mutex::Locker evaluation_locker(m_condition_mutex); + + size_t condition_hash; + const char *condition_text = GetConditionText(&condition_hash); + + if (!condition_text) + { + m_user_expression_sp.reset(); + return false; + } + + if (condition_hash != m_condition_hash || + !m_user_expression_sp || + !m_user_expression_sp->MatchesContext(exe_ctx)) + { + m_user_expression_sp.reset(new ClangUserExpression(condition_text, + NULL, + lldb::eLanguageTypeUnknown, + ClangUserExpression::eResultTypeAny)); + + StreamString errors; + + if (!m_user_expression_sp->Parse(errors, + exe_ctx, + eExecutionPolicyOnlyWhenNeeded, + true)) + { + error.SetErrorStringWithFormat("Couldn't parse conditional expression:\n%s", + errors.GetData()); + m_user_expression_sp.reset(); + return false; + } + + 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; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + const bool try_all_threads = true; + + Error expr_error; + + StreamString execution_errors; + + ClangExpressionVariableSP result_variable_sp; + + ExecutionResults result_code = + m_user_expression_sp->Execute(execution_errors, + exe_ctx, + unwind_on_error, + ignore_breakpoints, + m_user_expression_sp, + result_variable_sp, + try_all_threads, + ClangUserExpression::kDefaultTimeout); + + bool ret; + + if (result_code == eExecutionCompleted) + { + if (!result_variable_sp) + { + ret = false; + error.SetErrorString("Expression did not return a result"); + return false; + } + + result_value_sp = result_variable_sp->GetValueObject(); + + if (result_value_sp) + { + Scalar scalar_value; + if (result_value_sp->ResolveValue (scalar_value)) + { + if (scalar_value.ULongLong(1) == 0) + ret = false; + else + ret = true; + if (log) + log->Printf("Condition successfully evaluated, result is %s.\n", + ret ? "true" : "false"); + } + else + { + ret = false; + error.SetErrorString("Failed to get an integer result from the expression"); + } + } + else + { + ret = false; + error.SetErrorString("Failed to get any result from the expression"); + } + } + else + { + ret = false; + error.SetErrorStringWithFormat("Couldn't execute expression:\n%s", execution_errors.GetData()); + } + + return ret; +} + +uint32_t +BreakpointLocation::GetIgnoreCount () +{ + return GetOptionsNoCreate()->GetIgnoreCount(); +} + +void +BreakpointLocation::SetIgnoreCount (uint32_t n) +{ + GetLocationOptions()->SetIgnoreCount(n); + SendBreakpointLocationChangedEvent (eBreakpointEventTypeIgnoreChanged); +} + +void +BreakpointLocation::DecrementIgnoreCount() +{ + if (m_options_ap.get() != NULL) + { + uint32_t loc_ignore = m_options_ap->GetIgnoreCount(); + if (loc_ignore != 0) + m_options_ap->SetIgnoreCount(loc_ignore - 1); + } +} + +bool +BreakpointLocation::IgnoreCountShouldStop() +{ + if (m_options_ap.get() != NULL) + { + uint32_t loc_ignore = m_options_ap->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; +} + +const BreakpointOptions * +BreakpointLocation::GetOptionsNoCreate () const +{ + if (m_options_ap.get() != NULL) + return m_options_ap.get(); + else + return m_owner.GetOptions (); +} + +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_ap.get() == NULL) + m_options_ap.reset(BreakpointOptions::CopyOptionsNoCallback(*m_owner.GetOptions ())); + + return m_options_ap.get(); +} + +bool +BreakpointLocation::ValidForThisThread (Thread *thread) +{ + return thread->MatchesSpec(GetOptionsNoCreate()->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); + + IncrementHitCount(); + + 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; +} + +bool +BreakpointLocation::IsResolved () const +{ + return m_bp_site_sp.get() != NULL; +} + +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 == NULL) + return false; + + lldb::break_id_t new_id = process->CreateBreakpointSite (shared_from_this(), false); + + if (new_id == LLDB_INVALID_BREAK_ID) + { + Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + if (log) + log->Warning ("Tried to add breakpoint site at 0x%" PRIx64 " but it was already present.\n", + m_address.GetOpcodeLoadAddress (&m_owner.GetTarget())); + return false; + } + + return true; +} + +bool +BreakpointLocation::SetBreakpointSite (BreakpointSiteSP& bp_site_sp) +{ + m_bp_site_sp = bp_site_sp; + return true; +} + +bool +BreakpointLocation::ClearBreakpointSite () +{ + if (m_bp_site_sp.get()) + { + m_owner.GetTarget().GetProcessSP()->RemoveOwnerFromBreakpointSite (GetBreakpoint().GetID(), + GetID(), m_bp_site_sp); + 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) + { + s->PutCString("where = "); + sc.DumpStopContext (s, m_owner.GetTarget().GetProcessSP().get(), m_address, false, true, false); + } + else + { + if (sc.module_sp) + { + s->EOL(); + s->Indent("module = "); + sc.module_sp->GetFileSpec().Dump (s); + } + + if (sc.comp_unit != NULL) + { + s->EOL(); + s->Indent("compile unit = "); + static_cast(sc.comp_unit)->GetFilename().Dump (s); + + if (sc.function != NULL) + { + s->EOL(); + s->Indent("function = "); + s->PutCString (sc.function->GetMangled().GetName().AsCString("")); + } + + 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(); + s->Indent("symbol = "); + s->PutCString(sc.symbol->GetMangled().GetName().AsCString("")); + } + } + } + } + + if (level == lldb::eDescriptionLevelVerbose) + { + s->EOL(); + s->Indent(); + } + + if (m_address.IsSectionOffset() && (level == eDescriptionLevelFull || level == eDescriptionLevelInitial)) + s->Printf (", "); + s->Printf ("address = "); + + ExecutionContextScope *exe_scope = NULL; + Target *target = &m_owner.GetTarget(); + if (target) + exe_scope = target->GetProcessSP().get(); + if (exe_scope == NULL) + exe_scope = target; + + if (eDescriptionLevelInitial) + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleFileAddress); + else + m_address.Dump(s, exe_scope, Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); + + 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_ap.get()) + { + s->Indent(); + m_options_ap->GetDescription (s, level); + s->EOL(); + } + s->IndentLess(); + } + else if (level != eDescriptionLevelInitial) + { + s->Printf(", %sresolved, hit count = %u ", + (IsResolved() ? "" : "un"), + GetHitCount()); + if (m_options_ap.get()) + { + m_options_ap->GetDescription (s, level); + } + } +} + +void +BreakpointLocation::Dump(Stream *s) const +{ + if (s == NULL) + return; + + 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(), + GetOptionsNoCreate()->GetThreadSpecNoCreate()->GetTID(), + (uint64_t) m_address.GetOpcodeLoadAddress (&m_owner.GetTarget()), + (m_options_ap.get() ? m_options_ap->IsEnabled() : m_owner.IsEnabled()) ? "enabled " : "disabled", + IsHardware() ? "hardware" : "software", + GetHardwareIndex(), + GetHitCount(), + GetOptionsNoCreate()->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); + } +} + diff --git a/source/Breakpoint/BreakpointLocationCollection.cpp b/source/Breakpoint/BreakpointLocationCollection.cpp new file mode 100644 index 00000000000..ee3f56f928d --- /dev/null +++ b/source/Breakpoint/BreakpointLocationCollection.cpp @@ -0,0 +1,198 @@ +//===-- BreakpointLocationCollection.cpp ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationCollection.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.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() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +BreakpointLocationCollection::~BreakpointLocationCollection() +{ +} + +void +BreakpointLocationCollection::Add(const BreakpointLocationSP &bp_loc) +{ + 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) +{ + 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) +{ + 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 +{ + 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; + const size_t count = GetSize(); + for (size_t i = 0; i < count; i++) + { + if (GetByIndex(i)->ShouldStop(context)) + shouldStop = true; + } + return shouldStop; +} + +bool +BreakpointLocationCollection::ValidForThisThread (Thread *thread) +{ + 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 +{ + 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) +{ + 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); + } +} diff --git a/source/Breakpoint/BreakpointLocationList.cpp b/source/Breakpoint/BreakpointLocationList.cpp new file mode 100644 index 00000000000..22a4ff0c68e --- /dev/null +++ b/source/Breakpoint/BreakpointLocationList.cpp @@ -0,0 +1,305 @@ +//===-- BreakpointLocationList.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocationList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Core/Section.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointLocationList::BreakpointLocationList(Breakpoint &owner) : + m_owner (owner), + m_locations(), + m_address_to_location (), + m_mutex (Mutex::eMutexTypeRecursive), + m_next_id (0), + m_new_location_recorder (NULL) +{ +} + +BreakpointLocationList::~BreakpointLocationList() +{ +} + +BreakpointLocationSP +BreakpointLocationList::Create (const Address &addr) +{ + Mutex::Locker locker (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)); + 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; +} + +BreakpointLocationSP +BreakpointLocationList::FindByID (lldb::break_id_t break_id) const +{ + BreakpointLocationSP bp_loc_sp; + Mutex::Locker locker (m_mutex); + // We never remove a breakpoint locations, so the ID can be translated into + // the location index by subtracting 1 + uint32_t idx = break_id - 1; + if (idx <= m_locations.size()) + { + bp_loc_sp = m_locations[idx]; + } + return bp_loc_sp; +} + +size_t +BreakpointLocationList::FindInModule (Module *module, + BreakpointLocationCollection& bp_loc_list) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (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: ", this); + //s->Indent(); + Mutex::Locker locker (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).get()->Dump(s); + s->IndentLess(); +} + + +BreakpointLocationSP +BreakpointLocationList::GetByIndex (size_t i) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (m_mutex); + BreakpointLocationSP bp_loc_sp; + if (i < m_locations.size()) + bp_loc_sp = m_locations[i]; + + return bp_loc_sp; +} + +void +BreakpointLocationList::ClearAllBreakpointSites () +{ + Mutex::Locker locker (m_mutex); + collection::iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + (*pos)->ClearBreakpointSite(); +} + +void +BreakpointLocationList::ResolveAllBreakpointSites () +{ + Mutex::Locker locker (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; + Mutex::Locker locker (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 +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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 *new_location) +{ + Mutex::Locker locker (m_mutex); + + if (new_location) + *new_location = false; + BreakpointLocationSP bp_loc_sp (FindByAddress(addr)); + if (!bp_loc_sp) + { + bp_loc_sp = Create (addr); + 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; +} + +bool +BreakpointLocationList::RemoveLocation (const lldb::BreakpointLocationSP &bp_loc_sp) +{ + if (bp_loc_sp) + { + Mutex::Locker locker (m_mutex); + + m_address_to_location.erase (bp_loc_sp->GetAddress()); + + collection::iterator pos, end = m_locations.end(); + for (pos = m_locations.begin(); pos != end; ++pos) + { + if ((*pos).get() == bp_loc_sp.get()) + { + m_locations.erase (pos); + return true; + } + } + } + return false; +} + + + +void +BreakpointLocationList::StartRecordingNewLocations (BreakpointLocationCollection &new_locations) +{ + Mutex::Locker locker (m_mutex); + assert (m_new_location_recorder == NULL); + m_new_location_recorder = &new_locations; +} + +void +BreakpointLocationList::StopRecordingNewLocations () +{ + Mutex::Locker locker (m_mutex); + m_new_location_recorder = NULL; +} + diff --git a/source/Breakpoint/BreakpointOptions.cpp b/source/Breakpoint/BreakpointOptions.cpp new file mode 100644 index 00000000000..3a4a117695f --- /dev/null +++ b/source/Breakpoint/BreakpointOptions.cpp @@ -0,0 +1,298 @@ +//===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointOptions.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Value.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +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() : + m_callback (BreakpointOptions::NullCallback), + m_callback_baton_sp (), + m_callback_is_synchronous (false), + m_enabled (true), + m_one_shot (false), + m_ignore_count (0), + m_thread_spec_ap (), + m_condition_text (), + m_condition_text_hash (0) +{ +} + +//---------------------------------------------------------------------- +// BreakpointOptions copy constructor +//---------------------------------------------------------------------- +BreakpointOptions::BreakpointOptions(const BreakpointOptions& 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_enabled (rhs.m_enabled), + m_one_shot (rhs.m_one_shot), + m_ignore_count (rhs.m_ignore_count), + m_thread_spec_ap () +{ + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); + 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_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_ap.get() != NULL) + m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); + m_condition_text = rhs.m_condition_text; + m_condition_text_hash = rhs.m_condition_text_hash; + return *this; +} + +BreakpointOptions * +BreakpointOptions::CopyOptionsNoCallback (BreakpointOptions &orig) +{ + BreakpointHitCallback 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(); + BreakpointOptions *ret_val = new BreakpointOptions(orig); + + orig.SetCallback (orig_callback, orig_callback_baton_sp, orig_is_sync); + + return ret_val; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +BreakpointOptions::~BreakpointOptions() +{ +} + +//------------------------------------------------------------------ +// Callbacks +//------------------------------------------------------------------ +void +BreakpointOptions::SetCallback (BreakpointHitCallback 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 +BreakpointOptions::ClearCallback () +{ + m_callback = BreakpointOptions::NullCallback; + m_callback_is_synchronous = false; + m_callback_baton_sp.reset(); +} + +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 && context->is_synchronous == IsCallbackSynchronous()) + { + return m_callback (m_callback_baton_sp ? m_callback_baton_sp->m_data : NULL, + context, + break_id, + break_loc_id); + } + else + return true; +} + +bool +BreakpointOptions::HasCallback () +{ + return m_callback != BreakpointOptions::NullCallback; +} + +void +BreakpointOptions::SetCondition (const char *condition) +{ + if (!condition) + condition = ""; + + m_condition_text.assign(condition); + std::hash 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 NULL; + } +} + +const ThreadSpec * +BreakpointOptions::GetThreadSpecNoCreate () const +{ + return m_thread_spec_ap.get(); +} + +ThreadSpec * +BreakpointOptions::GetThreadSpec () +{ + if (m_thread_spec_ap.get() == NULL) + m_thread_spec_ap.reset (new ThreadSpec()); + + return m_thread_spec_ap.get(); +} + +void +BreakpointOptions::SetThreadID (lldb::tid_t thread_id) +{ + GetThreadSpec()->SetTID(thread_id); +} + +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 || (GetThreadSpecNoCreate() != NULL && 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_thread_spec_ap.get()) + m_thread_spec_ap->GetDescription (s, level); + else if (level == eDescriptionLevelBrief) + s->PutCString ("thread spec: no "); + 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 +{ + CommandData *data = (CommandData *)m_data; + + if (level == eDescriptionLevelBrief) + { + s->Printf (", commands = %s", (data && data->user_source.GetSize() > 0) ? "yes" : "no"); + return; + } + + s->IndentMore (); + s->Indent("Breakpoint 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/source/Breakpoint/BreakpointResolver.cpp b/source/Breakpoint/BreakpointResolver.cpp new file mode 100644 index 00000000000..b22fa1e6dbc --- /dev/null +++ b/source/Breakpoint/BreakpointResolver.cpp @@ -0,0 +1,61 @@ +//===-- BreakpointResolver.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolver.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Address.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolver: +//---------------------------------------------------------------------- +BreakpointResolver::BreakpointResolver (Breakpoint *bkpt, const unsigned char resolverTy) : + m_breakpoint (bkpt), + SubclassID (resolverTy) +{ +} + +BreakpointResolver::~BreakpointResolver () +{ + +} + +void +BreakpointResolver::SetBreakpoint (Breakpoint *bkpt) +{ + m_breakpoint = bkpt; +} + +void +BreakpointResolver::ResolveBreakpointInModules (SearchFilter &filter, ModuleList &modules) +{ + filter.SearchInModuleList(*this, modules); +} + +void +BreakpointResolver::ResolveBreakpoint (SearchFilter &filter) +{ + filter.Search (*this); +} + diff --git a/source/Breakpoint/BreakpointResolverAddress.cpp b/source/Breakpoint/BreakpointResolverAddress.cpp new file mode 100644 index 00000000000..1bcef93aeda --- /dev/null +++ b/source/Breakpoint/BreakpointResolverAddress.cpp @@ -0,0 +1,111 @@ +//===-- BreakpointResolverAddress.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverAddress.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" + +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverAddress: +//---------------------------------------------------------------------- +BreakpointResolverAddress::BreakpointResolverAddress +( + Breakpoint *bkpt, + const Address &addr +) : + BreakpointResolver (bkpt, BreakpointResolver::AddressResolver), + m_addr (addr) +{ +} + +BreakpointResolverAddress::~BreakpointResolverAddress () +{ + +} + +void +BreakpointResolverAddress::ResolveBreakpoint (SearchFilter &filter) +{ + // The address breakpoint only takes once, so if we've already set it we're done. + if (m_breakpoint->GetNumLocations() > 0) + return; + else + BreakpointResolver::ResolveBreakpoint(filter); +} + +void +BreakpointResolverAddress::ResolveBreakpointInModules +( + SearchFilter &filter, + ModuleList &modules +) +{ + // The address breakpoint only takes once, so if we've already set it we're done. + if (m_breakpoint->GetNumLocations() > 0) + return; + else + BreakpointResolver::ResolveBreakpointInModules (filter, modules); +} + +Searcher::CallbackReturn +BreakpointResolverAddress::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + assert (m_breakpoint != NULL); + + if (filter.AddressPasses (m_addr)) + { + BreakpointLocationSP bp_loc_sp(m_breakpoint->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()); + } + } + return Searcher::eCallbackReturnStop; +} + +Searcher::Depth +BreakpointResolverAddress::GetDepth() +{ + return Searcher::eDepthTarget; +} + +void +BreakpointResolverAddress::GetDescription (Stream *s) +{ + s->PutCString ("address = "); + m_addr.Dump(s, m_breakpoint->GetTarget().GetProcessSP().get(), Address::DumpStyleLoadAddress, Address::DumpStyleModuleWithFileAddress); +} + +void +BreakpointResolverAddress::Dump (Stream *s) const +{ + +} diff --git a/source/Breakpoint/BreakpointResolverFileLine.cpp b/source/Breakpoint/BreakpointResolverFileLine.cpp new file mode 100644 index 00000000000..91a218fdb80 --- /dev/null +++ b/source/Breakpoint/BreakpointResolverFileLine.cpp @@ -0,0 +1,246 @@ +//===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverFileLine.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverFileLine: +//---------------------------------------------------------------------- +BreakpointResolverFileLine::BreakpointResolverFileLine +( + Breakpoint *bkpt, + const FileSpec &file_spec, + uint32_t line_no, + bool check_inlines, + bool skip_prologue +) : + BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver), + m_file_spec (file_spec), + m_line_number (line_no), + m_inlines (check_inlines), + m_skip_prologue(skip_prologue) +{ +} + +BreakpointResolverFileLine::~BreakpointResolverFileLine () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileLine::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + SymbolContextList sc_list; + + assert (m_breakpoint != NULL); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + // 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. + + 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 (m_file_spec, m_line_number, m_inlines, false, eSymbolContextEverything, sc_list); + } + } + + while (sc_list.GetSize() > 0) + { + SymbolContextList tmp_sc_list; + unsigned current_idx = 0; + SymbolContext sc; + bool first_entry = true; + + FileSpec match_file_spec; + uint32_t closest_line_number = UINT32_MAX; + + // Pull out the first entry, and all the others that match its file spec, and stuff them in the tmp list. + while (current_idx < sc_list.GetSize()) + { + bool matches; + + sc_list.GetContextAtIndex (current_idx, sc); + if (first_entry) + { + match_file_spec = sc.line_entry.file; + matches = true; + first_entry = false; + } + else + matches = (sc.line_entry.file == match_file_spec); + + if (matches) + { + tmp_sc_list.Append (sc); + sc_list.RemoveContextAtIndex(current_idx); + + // ResolveSymbolContext will always return a number that is >= the line number you pass in. + // So the smaller line number is always better. + if (sc.line_entry.line < closest_line_number) + closest_line_number = sc.line_entry.line; + } + else + current_idx++; + } + + // Okay, we've found the closest line number match, now throw away all the others: + + current_idx = 0; + while (current_idx < tmp_sc_list.GetSize()) + { + if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) + { + if (sc.line_entry.line != closest_line_number) + tmp_sc_list.RemoveContextAtIndex(current_idx); + else + current_idx++; + } + } + + // Next go through and see if there are line table entries that are contiguous, and if so keep only the + // first of the contiguous range: + + lldb::addr_t last_end_addr = LLDB_INVALID_ADDRESS; + current_idx = 0; + while (current_idx < tmp_sc_list.GetSize()) + { + if (tmp_sc_list.GetContextAtIndex(current_idx, sc)) + { + lldb::addr_t start_file_addr = sc.line_entry.range.GetBaseAddress().GetFileAddress(); + lldb::addr_t end_file_addr = start_file_addr + sc.line_entry.range.GetByteSize(); + + if (start_file_addr == last_end_addr) + tmp_sc_list.RemoveContextAtIndex(current_idx); + else + current_idx++; + + last_end_addr = end_file_addr; + } + } + + // and make breakpoints out of the closest line number match. + + uint32_t tmp_sc_list_size = tmp_sc_list.GetSize(); + + for (uint32_t i = 0; i < tmp_sc_list_size; i++) + { + if (tmp_sc_list.GetContextAtIndex(i, sc)) + { + Address line_start = sc.line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + if (filter.AddressPasses(line_start)) + { + // If the line number is before the prologue end, move it there... + bool skipped_prologue = false; + if (m_skip_prologue) + { + if (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 (m_breakpoint->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()); + } + } + else if (log) + { + log->Printf ("Breakpoint at file address 0x%" PRIx64 " for %s:%d didn't pass the filter.\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString(""), + m_line_number); + } + } + else + { + if (log) + log->Printf ("error: Unable to set breakpoint at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + m_file_spec.GetFilename().AsCString(""), + m_line_number); + } + } + } + } + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileLine::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("file = '%s', line = %u", m_file_spec.GetPath().c_str(), m_line_number); +} + +void +BreakpointResolverFileLine::Dump (Stream *s) const +{ + +} + diff --git a/source/Breakpoint/BreakpointResolverFileRegex.cpp b/source/Breakpoint/BreakpointResolverFileRegex.cpp new file mode 100644 index 00000000000..de974d04894 --- /dev/null +++ b/source/Breakpoint/BreakpointResolverFileRegex.cpp @@ -0,0 +1,134 @@ +//===-- BreakpointResolverFileRegex.cpp --------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverFileRegex.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// BreakpointResolverFileRegex: +//---------------------------------------------------------------------- +BreakpointResolverFileRegex::BreakpointResolverFileRegex +( + Breakpoint *bkpt, + RegularExpression ®ex +) : + BreakpointResolver (bkpt, BreakpointResolver::FileLineResolver), + m_regex (regex) +{ +} + +BreakpointResolverFileRegex::~BreakpointResolverFileRegex () +{ +} + +Searcher::CallbackReturn +BreakpointResolverFileRegex::SearchCallback +( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool containing +) +{ + + assert (m_breakpoint != NULL); + if (!context.target_sp) + return eCallbackReturnContinue; + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS)); + + CompileUnit *cu = context.comp_unit; + FileSpec cu_file_spec = *(static_cast(cu)); + std::vector 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++) + { + uint32_t start_idx = 0; + bool exact = false; + while (1) + { + LineEntry line_entry; + + // Cycle through all the line entries that might match this one: + start_idx = cu->FindLineEntry (start_idx, line_matches[i], NULL, exact, &line_entry); + if (start_idx == UINT32_MAX) + break; + exact = true; + start_idx++; + + Address line_start = line_entry.range.GetBaseAddress(); + if (line_start.IsValid()) + { + if (filter.AddressPasses(line_start)) + { + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(line_start)); + if (log && bp_loc_sp && !m_breakpoint->IsInternal()) + { + StreamString s; + bp_loc_sp->GetDescription (&s, lldb::eDescriptionLevelVerbose); + log->Printf ("Added location: %s\n", s.GetData()); + } + } + else if (log) + { + log->Printf ("Breakpoint at file address 0x%" PRIx64 " for %s:%d didn't pass filter.\n", + line_start.GetFileAddress(), + cu_file_spec.GetFilename().AsCString(""), + line_matches[i]); + } + } + else + { + if (log) + log->Printf ("error: Unable to set breakpoint at file address 0x%" PRIx64 " for %s:%d\n", + line_start.GetFileAddress(), + cu_file_spec.GetFilename().AsCString(""), + line_matches[i]); + } + + } + } + assert (m_breakpoint != NULL); + + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +BreakpointResolverFileRegex::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +BreakpointResolverFileRegex::GetDescription (Stream *s) +{ + s->Printf ("source regex = \"%s\"", m_regex.GetText()); +} + +void +BreakpointResolverFileRegex::Dump (Stream *s) const +{ + +} + diff --git a/source/Breakpoint/BreakpointResolverName.cpp b/source/Breakpoint/BreakpointResolverName.cpp new file mode 100644 index 00000000000..27f85653d64 --- /dev/null +++ b/source/Breakpoint/BreakpointResolverName.cpp @@ -0,0 +1,357 @@ +//===-- BreakpointResolverName.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointResolverName.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" + +using namespace lldb; +using namespace lldb_private; + +BreakpointResolverName::BreakpointResolverName (Breakpoint *bkpt, + const char *name_cstr, + uint32_t name_type_mask, + Breakpoint::MatchType type, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (), + m_regex (), + m_match_type (type), + m_skip_prologue (skip_prologue) +{ + + if (m_match_type == Breakpoint::Regexp) + { + if (!m_regex.Compile (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, + uint32_t name_type_mask, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_match_type (Breakpoint::Exact), + 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 names, + uint32_t name_type_mask, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_match_type (Breakpoint::Exact), + 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, + bool skip_prologue) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (NULL), + m_regex (func_regex), + m_match_type (Breakpoint::Regexp), + m_skip_prologue (skip_prologue) +{ +} + +BreakpointResolverName::BreakpointResolverName +( + Breakpoint *bkpt, + const char *class_name, + const char *method, + Breakpoint::MatchType type, + bool skip_prologue +) : + BreakpointResolver (bkpt, BreakpointResolver::NameResolver), + m_class_name (class_name), + m_regex (), + m_match_type (type), + m_skip_prologue (skip_prologue) +{ + LookupInfo lookup; + lookup.name.SetCString(method); + lookup.lookup_name = lookup.name; + lookup.name_type_mask = eFunctionNameTypeMethod; + lookup.match_name_after_lookup = false; + m_lookups.push_back (lookup); +} + +BreakpointResolverName::~BreakpointResolverName () +{ +} + +void +BreakpointResolverName::AddNameLookup (const ConstString &name, uint32_t name_type_mask) +{ + ObjCLanguageRuntime::MethodName objc_method(name.GetCString(), false); + if (objc_method.IsValid(false)) + { + std::vector objc_names; + objc_method.GetFullNames(objc_names, true); + for (ConstString objc_name : objc_names) + { + LookupInfo lookup; + lookup.name = name; + lookup.lookup_name = objc_name; + lookup.name_type_mask = eFunctionNameTypeFull; + lookup.match_name_after_lookup = false; + m_lookups.push_back (lookup); + } + } + else + { + LookupInfo lookup; + lookup.name = name; + Module::PrepareForFunctionNameLookup(lookup.name, name_type_mask, lookup.lookup_name, lookup.name_type_mask, lookup.match_name_after_lookup); + m_lookups.push_back (lookup); + } +} + + +void +BreakpointResolverName::LookupInfo::Prune (SymbolContextList &sc_list, size_t start_idx) const +{ + if (match_name_after_lookup && 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(), name.GetCString()) == NULL) + { + sc_list.RemoveContextAtIndex(i); + } + else + { + ++i; + } + } + } +} + + +// 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 != NULL); + + 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; + const bool include_symbols = filter_by_cu == false; + const bool include_inlines = true; + const bool append = true; + + switch (m_match_type) + { + case Breakpoint::Exact: + if (context.module_sp) + { + for (const LookupInfo &lookup : m_lookups) + { + const size_t start_func_idx = func_list.GetSize(); + context.module_sp->FindFunctions (lookup.lookup_name, + NULL, + lookup.name_type_mask, + 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 filterning 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) + { + uint32_t num_functions = func_list.GetSize(); + + for (size_t idx = 0; idx < num_functions; idx++) + { + SymbolContext sc; + func_list.GetContextAtIndex(idx, sc); + if (!sc.comp_unit || !filter.CompUnitPasses(*sc.comp_unit)) + { + func_list.RemoveContextAtIndex(idx); + num_functions--; + idx--; + } + } + } + + // Remove any duplicates between the funcion 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)) + { + 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) + { + 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); + } + } + + if (break_addr.IsValid()) + { + if (filter.AddressPasses(break_addr)) + { + BreakpointLocationSP bp_loc_sp (m_breakpoint->AddLocation(break_addr, &new_location)); + 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; +} + +Searcher::Depth +BreakpointResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +void +BreakpointResolverName::GetDescription (Stream *s) +{ + if (m_match_type == Breakpoint::Regexp) + s->Printf("regex = '%s'", m_regex.GetText()); + else + { + size_t num_names = m_lookups.size(); + if (num_names == 1) + s->Printf("name = '%s'", m_lookups[0].name.GetCString()); + else + { + s->Printf("names = {"); + for (size_t i = 0; i < num_names - 1; i++) + { + s->Printf ("'%s', ", m_lookups[i].name.GetCString()); + } + s->Printf ("'%s'}", m_lookups[num_names - 1].name.GetCString()); + } + } +} + +void +BreakpointResolverName::Dump (Stream *s) const +{ + +} + diff --git a/source/Breakpoint/BreakpointSite.cpp b/source/Breakpoint/BreakpointSite.cpp new file mode 100644 index 00000000000..fa5d8c1f9f8 --- /dev/null +++ b/source/Breakpoint/BreakpointSite.cpp @@ -0,0 +1,234 @@ +//===-- BreakpointSite.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointSite.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSiteList.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.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(); + return m_owners.ShouldStop (context); +} + +bool +BreakpointSite::IsBreakpointAtThisSite (lldb::break_id_t bp_id) +{ + 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 == NULL) + 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) +{ + 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) +{ + m_owners.Add(owner); +} + +size_t +BreakpointSite::RemoveOwner (lldb::break_id_t break_id, lldb::break_id_t break_loc_id) +{ + m_owners.Remove(break_id, break_loc_id); + return m_owners.GetSize(); +} + +size_t +BreakpointSite::GetNumberOfOwners () +{ + return m_owners.GetSize(); +} + +BreakpointLocationSP +BreakpointSite::GetOwnerAtIndex (size_t index) +{ + return m_owners.GetByIndex (index); +} + +bool +BreakpointSite::ValidForThisThread (Thread *thread) +{ + return m_owners.ValidForThisThread(thread); +} + +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(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(bp_end_addr, end_addr) - m_addr; + if (opcode_offset) + *opcode_offset = 0; + } + } + return true; + } + } + return false; +} diff --git a/source/Breakpoint/BreakpointSiteList.cpp b/source/Breakpoint/BreakpointSiteList.cpp new file mode 100644 index 00000000000..68c4af18ec5 --- /dev/null +++ b/source/Breakpoint/BreakpointSiteList.cpp @@ -0,0 +1,240 @@ +//===-- BreakpointSiteList.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/BreakpointSiteList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include + +using namespace lldb; +using namespace lldb_private; + +BreakpointSiteList::BreakpointSiteList() : + m_mutex (Mutex::eMutexTypeRecursive), + 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(); + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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 val_pair) const + { + return m_break_id == val_pair.second.get()->GetID(); + } + +private: + const lldb::break_id_t m_break_id; +}; + +BreakpointSiteList::collection::iterator +BreakpointSiteList::GetIDIterator (lldb::break_id_t break_id) +{ + Mutex::Locker locker(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 +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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 +{ + Mutex::Locker locker(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; + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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: ", 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.get()->Dump(s); + s->IndentLess(); +} + +void +BreakpointSiteList::ForEach (std::function const &callback) +{ + Mutex::Locker locker(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; + + Mutex::Locker locker(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/source/Breakpoint/Stoppoint.cpp b/source/Breakpoint/Stoppoint.cpp new file mode 100644 index 00000000000..583ab47005f --- /dev/null +++ b/source/Breakpoint/Stoppoint.cpp @@ -0,0 +1,46 @@ +//===-- Stoppoint.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Breakpoint/Stoppoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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/source/Breakpoint/StoppointCallbackContext.cpp b/source/Breakpoint/StoppointCallbackContext.cpp new file mode 100644 index 00000000000..2266c3e429c --- /dev/null +++ b/source/Breakpoint/StoppointCallbackContext.cpp @@ -0,0 +1,39 @@ +//===-- StoppointCallbackContext.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointCallbackContext.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb_private; + +StoppointCallbackContext::StoppointCallbackContext() : + event (NULL), + 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 = NULL; + exe_ctx_ref.Clear(); + is_synchronous = false; +} diff --git a/source/Breakpoint/StoppointLocation.cpp b/source/Breakpoint/StoppointLocation.cpp new file mode 100644 index 00000000000..092caa5a932 --- /dev/null +++ b/source/Breakpoint/StoppointLocation.cpp @@ -0,0 +1,48 @@ +//===-- StoppointLocation.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/StoppointLocation.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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_hw_preferred(hardware), + m_hw_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_hw_preferred(hardware), + m_hw_index(LLDB_INVALID_INDEX32), + m_byte_size(byte_size), + m_hit_count(0) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +StoppointLocation::~StoppointLocation() +{ +} diff --git a/source/Breakpoint/Watchpoint.cpp b/source/Breakpoint/Watchpoint.cpp new file mode 100644 index 00000000000..45559b1901a --- /dev/null +++ b/source/Breakpoint/Watchpoint.cpp @@ -0,0 +1,489 @@ +//===-- Watchpoint.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/Watchpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.h" + +using namespace lldb; +using namespace lldb_private; + +Watchpoint::Watchpoint (Target& target, lldb::addr_t addr, uint32_t size, const ClangASTType *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() +{ +} + +// 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, BatonSP (new Baton(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; + return; +} + +std::string +Watchpoint::GetWatchSpec() +{ + return m_watch_spec_str; +} + +void +Watchpoint::SetWatchSpec (const std::string &str) +{ + m_watch_spec_str = str; + return; +} + +// 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.AsCString(), watch_address, m_type); + m_new_value_sp = m_new_value_sp->CreateConstantValue(watch_name); + if (m_new_value_sp && m_new_value_sp->GetError().Success()) + return true; + else + return false; +} + +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(); + + if (!IsEnabled()) + return false; + + if (GetHitCount() <= GetIgnoreCount()) + return false; + + return true; +} + +void +Watchpoint::GetDescription (Stream *s, lldb::DescriptionLevel level) +{ + DumpWithLevel(s, level); + return; +} + +void +Watchpoint::Dump(Stream *s) const +{ + DumpWithLevel(s, lldb::eDescriptionLevelBrief); +} + +// If prefix is NULL, 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) + { + s->Printf("\n%sold value: %s", prefix, m_old_value_sp->GetValueAsCString()); + } + if (m_new_value_sp) + { + s->Printf("\n%snew value: %s", prefix, m_new_value_sp->GetValueAsCString()); + } +} + +void +Watchpoint::DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) const +{ + if (s == NULL) + 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; +} + +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 == NULL || condition[0] == '\0') + { + if (m_condition_ap.get()) + m_condition_ap.reset(); + } + else + { + // Pass NULL for expr_prefix (no translation-unit level definitions). + m_condition_ap.reset(new ClangUserExpression (condition, NULL, lldb::eLanguageTypeUnknown, ClangUserExpression::eResultTypeAny)); + } + SendWatchpointChangedEvent (eWatchpointEventTypeConditionChanged); +} + +const char * +Watchpoint::GetConditionText () const +{ + if (m_condition_ap.get()) + return m_condition_ap->GetUserText(); + else + return NULL; +} + +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 == NULL) + 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 () +{ +} + +const ConstString & +Watchpoint::WatchpointEventData::GetFlavorString () +{ + static ConstString g_flavor ("Watchpoint::WatchpointEventData"); + return g_flavor; +} + +const 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 (event->GetData()); + } + return NULL; +} + +WatchpointEventType +Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent (const EventSP &event_sp) +{ + const WatchpointEventData *data = GetEventDataFromEvent (event_sp.get()); + + if (data == NULL) + 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/source/Breakpoint/WatchpointList.cpp b/source/Breakpoint/WatchpointList.cpp new file mode 100644 index 00000000000..6d62dffd22c --- /dev/null +++ b/source/Breakpoint/WatchpointList.cpp @@ -0,0 +1,306 @@ +//===-- WatchpointList.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Breakpoint/Watchpoint.h" + +using namespace lldb; +using namespace lldb_private; + +WatchpointList::WatchpointList() : + m_watchpoints (), + m_mutex (Mutex::eMutexTypeRecursive), + 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) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (m_mutex); + s->Printf("%p: ", 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; + Mutex::Locker locker (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)->GetLoadAddress() == addr) { + wp_sp = *pos; + break; + } + } + + return wp_sp; +} + +const WatchpointSP +WatchpointList::FindBySpec (std::string spec) const +{ + WatchpointSP wp_sp; + Mutex::Locker locker (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; + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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 +{ + Mutex::Locker locker (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 +WatchpointList::GetWatchpointIDs() const +{ + std::vector 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) +{ + Mutex::Locker locker (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; + Mutex::Locker locker (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) +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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 (Mutex::Locker &locker) +{ + return locker.Lock (m_mutex); +} diff --git a/source/Breakpoint/WatchpointOptions.cpp b/source/Breakpoint/WatchpointOptions.cpp new file mode 100644 index 00000000000..c2c9696c4ce --- /dev/null +++ b/source/Breakpoint/WatchpointOptions.cpp @@ -0,0 +1,241 @@ +//===-- WatchpointOptions.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Breakpoint/WatchpointOptions.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Value.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/ThreadSpec.h" +#include "lldb/Expression/ClangUserExpression.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_ap () +{ +} + +//---------------------------------------------------------------------- +// 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_ap () +{ + if (rhs.m_thread_spec_ap.get() != NULL) + m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get())); +} + +//---------------------------------------------------------------------- +// 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_ap.get() != NULL) + m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); + 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() +{ +} + +//------------------------------------------------------------------ +// 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->m_data : NULL, + context, + watch_id); + } + else + return true; +} + +bool +WatchpointOptions::HasCallback () +{ + return m_callback != WatchpointOptions::NullCallback; +} + +const ThreadSpec * +WatchpointOptions::GetThreadSpecNoCreate () const +{ + return m_thread_spec_ap.get(); +} + +ThreadSpec * +WatchpointOptions::GetThreadSpec () +{ + if (m_thread_spec_ap.get() == NULL) + m_thread_spec_ap.reset (new ThreadSpec()); + + return m_thread_spec_ap.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() != NULL && 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_ap.get()) + m_thread_spec_ap->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 +{ + CommandData *data = (CommandData *)m_data; + + 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/source/Commands/CommandCompletions.cpp b/source/Commands/CommandCompletions.cpp new file mode 100644 index 00000000000..a9d2f21b9ba --- /dev/null +++ b/source/Commands/CommandCompletions.cpp @@ -0,0 +1,754 @@ +//===-- CommandCompletions.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include +#if defined(__APPLE__) || defined(__linux__) +#include +#endif + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/CleanUp.h" + +using namespace lldb_private; + +CommandCompletions::CommonCompletionElement +CommandCompletions::g_common_completions[] = +{ + {eCustomCompletion, NULL}, + {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, NULL} // This one has to be last in the list. +}; + +bool +CommandCompletions::InvokeCommonCompletionCallbacks +( + CommandInterpreter &interpreter, + uint32_t completion_mask, + const char *completion_str, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + 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 != NULL) + { + handled = true; + g_common_completions[i].callback (interpreter, + completion_str, + match_start_point, + max_return_elements, + searcher, + word_complete, + matches); + } + } + return handled; +} + +int +CommandCompletions::SourceFiles +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + word_complete = true; + // Find some way to switch "include support files..." + SourceFileCompleter completer (interpreter, + false, + partial_file_name, + match_start_point, + max_return_elements, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +static int +DiskFilesOrDirectories +( + const char *partial_file_name, + bool only_directories, + bool &saw_directory, + StringList &matches +) +{ + // I'm going to use the "glob" function with GLOB_TILDE for user directory expansion. + // If it is not defined on your host system, you'll need to implement it yourself... + + size_t partial_name_len = strlen(partial_file_name); + + if (partial_name_len >= PATH_MAX) + return matches.GetSize(); + + // This copy of the string will be cut up into the directory part, and the remainder. end_ptr + // below will point to the place of the remainder in this string. Then when we've resolved the + // containing directory, and opened it, we'll read the directory contents and overwrite the + // partial_name_copy starting from end_ptr with each of the matches. Thus we will preserve + // the form the user originally typed. + + char partial_name_copy[PATH_MAX]; + memcpy(partial_name_copy, partial_file_name, partial_name_len); + partial_name_copy[partial_name_len] = '\0'; + + // We'll need to save a copy of the remainder for comparison, which we do here. + char remainder[PATH_MAX]; + + // end_ptr will point past the last / in partial_name_copy, or if there is no slash to the beginning of the string. + char *end_ptr; + + end_ptr = strrchr(partial_name_copy, '/'); + + // This will store the resolved form of the containing directory + char containing_part[PATH_MAX]; + + if (end_ptr == NULL) + { + // There's no directory. If the thing begins with a "~" then this is a bare + // user name. + if (*partial_name_copy == '~') + { + // Nothing here but the user name. We could just put a slash on the end, + // but for completeness sake we'll resolve the user name and only put a slash + // on the end if it exists. + char resolved_username[PATH_MAX]; + size_t resolved_username_len = FileSpec::ResolveUsername (partial_name_copy, resolved_username, + sizeof (resolved_username)); + + // Not sure how this would happen, a username longer than PATH_MAX? Still... + if (resolved_username_len >= sizeof (resolved_username)) + return matches.GetSize(); + else if (resolved_username_len == 0) + { + // The user name didn't resolve, let's look in the password database for matches. + // The user name database contains duplicates, and is not in alphabetical order, so + // we'll use a set to manage that for us. + FileSpec::ResolvePartialUsername (partial_name_copy, matches); + if (matches.GetSize() > 0) + saw_directory = true; + return matches.GetSize(); + } + else + { + //The thing exists, put a '/' on the end, and return it... + // FIXME: complete user names here: + partial_name_copy[partial_name_len] = '/'; + partial_name_copy[partial_name_len+1] = '\0'; + matches.AppendString(partial_name_copy); + saw_directory = true; + return matches.GetSize(); + } + } + else + { + // The containing part is the CWD, and the whole string is the remainder. + containing_part[0] = '.'; + containing_part[1] = '\0'; + strcpy(remainder, partial_name_copy); + end_ptr = partial_name_copy; + } + } + else + { + if (end_ptr == partial_name_copy) + { + // We're completing a file or directory in the root volume. + containing_part[0] = '/'; + containing_part[1] = '\0'; + } + else + { + size_t len = end_ptr - partial_name_copy; + memcpy(containing_part, partial_name_copy, len); + containing_part[len] = '\0'; + } + // Push end_ptr past the final "/" and set remainder. + end_ptr++; + strcpy(remainder, end_ptr); + } + + // Look for a user name in the containing part, and if it's there, resolve it and stick the + // result back into the containing_part: + + if (*partial_name_copy == '~') + { + size_t resolved_username_len = FileSpec::ResolveUsername(containing_part, + containing_part, + sizeof (containing_part)); + // User name doesn't exist, we're not getting any further... + if (resolved_username_len == 0 || resolved_username_len >= sizeof (containing_part)) + return matches.GetSize(); + } + + // Okay, containing_part is now the directory we want to open and look for files: + + lldb_utility::CleanUp dir_stream (opendir(containing_part), NULL, closedir); + if (!dir_stream.is_valid()) + return matches.GetSize(); + + struct dirent *dirent_buf; + + size_t baselen = end_ptr - partial_name_copy; + + while ((dirent_buf = readdir(dir_stream.get())) != NULL) + { + char *name = dirent_buf->d_name; + + // Omit ".", ".." and any . files if the match string doesn't start with . + if (name[0] == '.') + { + if (name[1] == '\0') + continue; + else if (name[1] == '.' && name[2] == '\0') + continue; + else if (remainder[0] != '.') + continue; + } + + // If we found a directory, we put a "/" at the end of the name. + + if (remainder[0] == '\0' || strstr(dirent_buf->d_name, remainder) == name) + { + if (strlen(name) + baselen >= PATH_MAX) + continue; + + strcpy(end_ptr, name); + + bool isa_directory = false; + if (dirent_buf->d_type & DT_DIR) + isa_directory = true; + else if (dirent_buf->d_type & DT_LNK) + { + struct stat stat_buf; + if ((stat(partial_name_copy, &stat_buf) == 0) && S_ISDIR(stat_buf.st_mode)) + isa_directory = true; + } + + if (isa_directory) + { + saw_directory = true; + size_t len = strlen(partial_name_copy); + partial_name_copy[len] = '/'; + partial_name_copy[len + 1] = '\0'; + } + if (only_directories && !isa_directory) + continue; + matches.AppendString(partial_name_copy); + } + } + + return matches.GetSize(); +} + +int +CommandCompletions::DiskFiles +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + + int ret_val = DiskFilesOrDirectories (partial_file_name, + false, + word_complete, + matches); + word_complete = !word_complete; + return ret_val; +} + +int +CommandCompletions::DiskDirectories +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + int ret_val = DiskFilesOrDirectories (partial_file_name, + true, + word_complete, + matches); + word_complete = false; + return ret_val; +} + +int +CommandCompletions::Modules +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches +) +{ + word_complete = true; + ModuleCompleter completer (interpreter, + partial_file_name, + match_start_point, + max_return_elements, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::Symbols +( + CommandInterpreter &interpreter, + const char *partial_file_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches) +{ + word_complete = true; + SymbolCompleter completer (interpreter, + partial_file_name, + match_start_point, + max_return_elements, + matches); + + if (searcher == NULL) + { + lldb::TargetSP target_sp = interpreter.GetDebugger().GetSelectedTarget(); + SearchFilter null_searcher (target_sp); + completer.DoCompletion (&null_searcher); + } + else + { + completer.DoCompletion (searcher); + } + return matches.GetSize(); +} + +int +CommandCompletions::SettingsNames (CommandInterpreter &interpreter, + const char *partial_setting_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + StringList &matches) +{ + // 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(NULL, 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; + const size_t num_matches = g_property_names.AutoComplete (partial_setting_name, matches, exact_matches_idx); + word_complete = exact_matches_idx != SIZE_MAX; + return num_matches; +} + + +int +CommandCompletions::PlatformPluginNames (CommandInterpreter &interpreter, + const char *partial_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches) +{ + const uint32_t num_matches = PluginManager::AutoCompletePlatformName(partial_name, matches); + word_complete = num_matches == 1; + return num_matches; +} + +int +CommandCompletions::ArchitectureNames (CommandInterpreter &interpreter, + const char *partial_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches) +{ + const uint32_t num_matches = ArchSpec::AutoComplete (partial_name, matches); + word_complete = num_matches == 1; + return num_matches; +} + + +int +CommandCompletions::VariablePath (CommandInterpreter &interpreter, + const char *partial_name, + int match_start_point, + int max_return_elements, + SearchFilter *searcher, + bool &word_complete, + lldb_private::StringList &matches) +{ + return Variable::AutoComplete (interpreter.GetExecutionContext(), partial_name, matches, word_complete); +} + + +CommandCompletions::Completer::Completer +( + CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + m_interpreter (interpreter), + m_completion_str (completion_str), + m_match_start_point (match_start_point), + m_max_return_elements (max_return_elements), + m_matches (matches) +{ +} + +CommandCompletions::Completer::~Completer () +{ + +} + +//---------------------------------------------------------------------- +// SourceFileCompleter +//---------------------------------------------------------------------- + +CommandCompletions::SourceFileCompleter::SourceFileCompleter +( + CommandInterpreter &interpreter, + bool include_support_files, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches), + m_include_support_files (include_support_files), + m_matching_files() +{ + FileSpec partial_spec (m_completion_str.c_str(), false); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::SourceFileCompleter::GetDepth() +{ + return eDepthCompUnit; +} + +Searcher::CallbackReturn +CommandCompletions::SourceFileCompleter::SearchCallback ( + SearchFilter &filter, + SymbolContext &context, + Address *addr, + bool complete +) +{ + if (context.comp_unit != NULL) + { + 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_matches.AppendString (m_matching_files.GetFileSpecAtIndex(i).GetFilename().GetCString()); + } + return m_matches.GetSize(); + +} + +//---------------------------------------------------------------------- +// SymbolCompleter +//---------------------------------------------------------------------- + +static bool +regex_chars (const char comp) +{ + if (comp == '[' || comp == ']' || + comp == '(' || comp == ')' || + comp == '{' || comp == '}' || + comp == '+' || + comp == '.' || + comp == '*' || + comp == '|' || + comp == '^' || + comp == '$' || + comp == '\\' || + comp == '?') + return true; + else + return false; +} +CommandCompletions::SymbolCompleter::SymbolCompleter +( + CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches) +{ + std::string regex_str; + if (completion_str && completion_str[0]) + { + regex_str.append("^"); + regex_str.append(completion_str); + } + 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.c_str()); +} + +Searcher::Depth +CommandCompletions::SymbolCompleter::GetDepth() +{ + return eDepthModule; +} + +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_matches.AppendString((*pos).GetCString()); + + return m_matches.GetSize(); +} + +//---------------------------------------------------------------------- +// ModuleCompleter +//---------------------------------------------------------------------- +CommandCompletions::ModuleCompleter::ModuleCompleter +( + CommandInterpreter &interpreter, + const char *completion_str, + int match_start_point, + int max_return_elements, + StringList &matches +) : + CommandCompletions::Completer (interpreter, completion_str, match_start_point, max_return_elements, matches) +{ + FileSpec partial_spec (m_completion_str.c_str(), false); + m_file_name = partial_spec.GetFilename().GetCString(); + m_dir_name = partial_spec.GetDirectory().GetCString(); +} + +Searcher::Depth +CommandCompletions::ModuleCompleter::GetDepth() +{ + return eDepthModule; +} + +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_matches.AppendString (cur_file_name); + } + } + return Searcher::eCallbackReturnContinue; +} + +size_t +CommandCompletions::ModuleCompleter::DoCompletion (SearchFilter *filter) +{ + filter->Search (*this); + return m_matches.GetSize(); +} diff --git a/source/Commands/CommandObjectApropos.cpp b/source/Commands/CommandObjectApropos.cpp new file mode 100644 index 00000000000..02dc7269775 --- /dev/null +++ b/source/Commands/CommandObjectApropos.cpp @@ -0,0 +1,154 @@ +//===-- CommandObjectApropos.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectApropos.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectApropos +//------------------------------------------------------------------------- + +CommandObjectApropos::CommandObjectApropos (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "apropos", + "Find a list of debugger commands related to a particular word/subject.", + NULL) +{ + 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() +{ +} + + +bool +CommandObjectApropos::DoExecute (Args& args, CommandReturnObject &result) +{ + const size_t argc = args.GetArgumentCount (); + + if (argc == 1) + { + const char *search_word = args.GetArgumentAtIndex(0); + if ((search_word != NULL) + && (strlen (search_word) > 0)) + { + // The bulk of the work must be done inside the Command Interpreter, since the command dictionary + // is private. + StringList commands_found; + StringList commands_help; + StringList user_commands_found; + StringList user_commands_help; + + m_interpreter.FindCommandsForApropos (search_word, commands_found, commands_help, true, false); + m_interpreter.FindCommandsForApropos (search_word, user_commands_found, user_commands_help, false, true); + + if (commands_found.GetSize() == 0 && user_commands_found.GetSize() == 0) + { + result.AppendMessageWithFormat ("No commands found pertaining to '%s'. Try 'help' to see a complete list of debugger commands.\n", search_word); + } + else + { + if (commands_found.GetSize() > 0) + { + result.AppendMessageWithFormat ("The following built-in commands may relate to '%s':\n", search_word); + 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); + if (user_commands_found.GetSize() > 0) + result.AppendMessage(""); + } + + if (user_commands_found.GetSize() > 0) + { + result.AppendMessageWithFormat ("The following user commands may relate to '%s':\n", search_word); + size_t max_len = 0; + + for (size_t i = 0; i < user_commands_found.GetSize(); ++i) + { + size_t len = strlen (user_commands_found.GetStringAtIndex (i)); + if (len > max_len) + max_len = len; + } + + for (size_t i = 0; i < user_commands_found.GetSize(); ++i) + m_interpreter.OutputFormattedHelpText (result.GetOutputStream(), + user_commands_found.GetStringAtIndex(i), + "--", + user_commands_help.GetStringAtIndex(i), + max_len); + } + + } + + + std::vector properties; + const size_t num_properties = m_interpreter.GetDebugger().Apropos(search_word, properties); + if (num_properties) + { + const bool dump_qualified_name = true; + result.AppendMessageWithFormat ("\nThe following settings variables may relate to '%s': \n\n", search_word); + for (size_t i=0; iDumpDescription (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/source/Commands/CommandObjectApropos.h b/source/Commands/CommandObjectApropos.h new file mode 100644 index 00000000000..f5154177bb2 --- /dev/null +++ b/source/Commands/CommandObjectApropos.h @@ -0,0 +1,44 @@ +//===-- CommandObjectApropos.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectApropos_h_ +#define liblldb_CommandObjectApropos_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectApropos +//------------------------------------------------------------------------- + +class CommandObjectApropos : public CommandObjectParsed +{ +public: + + CommandObjectApropos (CommandInterpreter &interpreter); + + virtual + ~CommandObjectApropos (); + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectApropos_h_ diff --git a/source/Commands/CommandObjectArgs.cpp b/source/Commands/CommandObjectArgs.cpp new file mode 100644 index 00000000000..05fd53bbe89 --- /dev/null +++ b/source/Commands/CommandObjectArgs.cpp @@ -0,0 +1,272 @@ +//===-- CommandObjectArgs.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectArgs.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Value.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/StackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +// This command is a toy. I'm just using it to have a way to construct the arguments to +// calling functions. +// + +CommandObjectArgs::CommandOptions::CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) +{ + // Keep only one place to reset the values to their defaults + OptionParsingStarting(); +} + + +CommandObjectArgs::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectArgs::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) +{ + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectArgs::CommandOptions::OptionParsingStarting () +{ +} + +const OptionDefinition* +CommandObjectArgs::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectArgs::CommandObjectArgs (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "args", + "When stopped at the start of a function, reads function arguments of type (u?)int(8|16|32|64)_t, (void|char)*", + "args"), + m_options (interpreter) +{ +} + +CommandObjectArgs::~CommandObjectArgs () +{ +} + +Options * +CommandObjectArgs::GetOptions () +{ + return &m_options; +} + +bool +CommandObjectArgs::DoExecute (Args& args, CommandReturnObject &result) +{ + ConstString target_triple; + + + Process *process = m_exe_ctx.GetProcessPtr(); + if (!process) + { + result.AppendError ("Args found no process."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const ABI *abi = process->GetABI().get(); + if (!abi) + { + result.AppendError ("The current process has no ABI."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const size_t num_args = args.GetArgumentCount (); + size_t arg_index; + + if (!num_args) + { + result.AppendError ("args requires at least one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Thread *thread = m_exe_ctx.GetThreadPtr(); + + if (!thread) + { + result.AppendError ("args found no thread."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + lldb::StackFrameSP thread_cur_frame = thread->GetSelectedFrame (); + if (!thread_cur_frame) + { + result.AppendError ("The current thread has no current frame."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ModuleSP thread_module_sp (thread_cur_frame->GetFrameCodeAddress ().GetModule()); + if (!thread_module_sp) + { + result.AppendError ("The PC has no associated module."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + ClangASTContext &ast_context = thread_module_sp->GetClangASTContext(); + + ValueList value_list; + + for (arg_index = 0; arg_index < num_args; ++arg_index) + { + const char *arg_type_cstr = args.GetArgumentAtIndex(arg_index); + Value value; + value.SetValueType(Value::eValueTypeScalar); + ClangASTType clang_type; + + char *int_pos; + if ((int_pos = strstr (const_cast(arg_type_cstr), "int"))) + { + Encoding encoding = eEncodingSint; + + int width = 0; + + if (int_pos > arg_type_cstr + 1) + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (int_pos == arg_type_cstr + 1 && arg_type_cstr[0] != 'u') + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + if (arg_type_cstr[0] == 'u') + { + encoding = eEncodingUint; + } + + char *width_pos = int_pos + 3; + + if (!strcmp (width_pos, "8_t")) + width = 8; + else if (!strcmp (width_pos, "16_t")) + width = 16; + else if (!strcmp (width_pos, "32_t")) + width = 32; + else if (!strcmp (width_pos, "64_t")) + width = 64; + else + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + + clang_type = ast_context.GetBuiltinTypeForEncodingAndBitSize(encoding, width); + + if (!clang_type.IsValid()) + { + result.AppendErrorWithFormat ("Couldn't get Clang type for format %s (%s integer, width %d).\n", + arg_type_cstr, + (encoding == eEncodingSint ? "signed" : "unsigned"), + width); + + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else if (strchr (arg_type_cstr, '*')) + { + if (!strcmp (arg_type_cstr, "void*")) + clang_type = ast_context.GetBasicType(eBasicTypeVoid).GetPointerType(); + else if (!strcmp (arg_type_cstr, "char*")) + clang_type = ast_context.GetCStringType (false); + else + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendErrorWithFormat ("Invalid format: %s.\n", arg_type_cstr); + result.SetStatus (eReturnStatusFailed); + return false; + } + + value.SetClangType (clang_type); + value_list.PushValue(value); + } + + if (!abi->GetArgumentValues (*thread, value_list)) + { + result.AppendError ("Couldn't get argument values"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + result.GetOutputStream ().Printf("Arguments : \n"); + + for (arg_index = 0; arg_index < num_args; ++arg_index) + { + result.GetOutputStream ().Printf ("%zu (%s): ", arg_index, args.GetArgumentAtIndex (arg_index)); + value_list.GetValueAtIndex (arg_index)->Dump (&result.GetOutputStream ()); + result.GetOutputStream ().Printf("\n"); + } + + return result.Succeeded(); +} + +OptionDefinition +CommandObjectArgs::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "debug", 'g', no_argument, NULL, 0, eArgTypeNone, "Enable verbose debug logging of the expression parsing and evaluation."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + diff --git a/source/Commands/CommandObjectArgs.h b/source/Commands/CommandObjectArgs.h new file mode 100644 index 00000000000..6691283ce09 --- /dev/null +++ b/source/Commands/CommandObjectArgs.h @@ -0,0 +1,72 @@ +//===-- CommandObjectArgs.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectArgs_h_ +#define liblldb_CommandObjectArgs_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/Language.h" + +namespace lldb_private { + + class CommandObjectArgs : public CommandObjectParsed + { + public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting (); + + const OptionDefinition* + GetDefinitions (); + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + }; + + CommandObjectArgs (CommandInterpreter &interpreter); + + virtual + ~CommandObjectArgs (); + + virtual + Options * + GetOptions (); + + + protected: + + CommandOptions m_options; + + virtual bool + DoExecute ( Args& command, + CommandReturnObject &result); + + }; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectArgs_h_ diff --git a/source/Commands/CommandObjectBreakpoint.cpp b/source/Commands/CommandObjectBreakpoint.cpp new file mode 100644 index 00000000000..cb70c99a195 --- /dev/null +++ b/source/Commands/CommandObjectBreakpoint.cpp @@ -0,0 +1,1843 @@ +//===-- CommandObjectBreakpoint.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectBreakpoint.h" +#include "CommandObjectBreakpointCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + +#include + +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(); +} + +//------------------------------------------------------------------------- +// CommandObjectBreakpointSet +//------------------------------------------------------------------------- + + +class CommandObjectBreakpointSet : public CommandObjectParsed +{ +public: + + typedef enum BreakpointSetType + { + eSetTypeInvalid, + eSetTypeFileAndLine, + eSetTypeAddress, + eSetTypeFunctionName, + eSetTypeFunctionRegexp, + eSetTypeSourceRegexp, + eSetTypeException + } BreakpointSetType; + + CommandObjectBreakpointSet (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint set", + "Sets a breakpoint or set of breakpoints in the executable.", + "breakpoint set "), + m_options (interpreter) + { + } + + + virtual + ~CommandObjectBreakpointSet () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + 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_ignore_count (0), + m_thread_id(LLDB_INVALID_THREAD_ID), + m_thread_index (UINT32_MAX), + m_thread_name(), + m_queue_name(), + m_catch_bp (false), + m_throw_bp (true), + m_language (eLanguageTypeUnknown), + m_skip_prologue (eLazyBoolCalculate), + m_one_shot (false) + { + } + + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_load_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + break; + + case 'b': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeBase; + break; + + case 'C': + m_column = Args::StringToUInt32 (option_arg, 0); + break; + + case 'c': + m_condition.assign(option_arg); + break; + + case 'E': + { + LanguageType language = LanguageRuntime::GetLanguageTypeFromString (option_arg); + + switch (language) + { + case eLanguageTypeC89: + case eLanguageTypeC: + case eLanguageTypeC99: + m_language = eLanguageTypeC; + break; + case eLanguageTypeC_plus_plus: + m_language = eLanguageTypeC_plus_plus; + break; + case eLanguageTypeObjC: + m_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); + break; + default: + error.SetErrorStringWithFormat ("Unsupported language type: '%s' for exception breakpoint", option_arg); + } + } + break; + + case 'f': + m_filenames.AppendIfUnique (FileSpec(option_arg, false)); + break; + + case 'F': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeFull; + break; + + case 'h': + { + bool success; + m_catch_bp = Args::StringToBoolean (option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat ("Invalid boolean value for on-catch option: '%s'", option_arg); + } + break; + case 'i': + { + m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_ignore_count == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg); + break; + } + + case 'K': + { + bool success; + bool value; + value = Args::StringToBoolean (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); + } + break; + + case 'l': + m_line_num = Args::StringToUInt32 (option_arg, 0); + 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 'o': + m_one_shot = true; + break; + + case 'p': + m_source_text_regexp.assign (option_arg); + break; + + case 'q': + m_queue_name.assign (option_arg); + break; + + case 'r': + m_func_regexp.assign (option_arg); + break; + + case 's': + { + m_modules.AppendIfUnique (FileSpec (option_arg, false)); + break; + } + + case 'S': + m_func_names.push_back (option_arg); + m_func_name_type_mask |= eFunctionNameTypeSelector; + break; + + case 't' : + { + m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); + if (m_thread_id == LLDB_INVALID_THREAD_ID) + error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); + } + break; + + case 'T': + m_thread_name.assign (option_arg); + break; + + case 'w': + { + bool success; + m_throw_bp = Args::StringToBoolean (option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat ("Invalid boolean value for on-throw option: '%s'", option_arg); + } + break; + + case 'x': + { + m_thread_index = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_thread_id == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); + + } + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + void + OptionParsingStarting () + { + m_condition.clear(); + 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_ignore_count = 0; + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_index = UINT32_MAX; + m_thread_name.clear(); + m_queue_name.clear(); + m_catch_bp = false; + m_throw_bp = true; + m_language = eLanguageTypeUnknown; + m_skip_prologue = eLazyBoolCalculate; + m_one_shot = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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 m_func_names; + uint32_t 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; + uint32_t m_ignore_count; + lldb::tid_t m_thread_id; + uint32_t m_thread_index; + std::string m_thread_name; + std::string m_queue_name; + bool m_catch_bp; + bool m_throw_bp; + lldb::LanguageType m_language; + LazyBool m_skip_prologue; + bool m_one_shot; + + }; + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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_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_language != eLanguageTypeUnknown) + break_type = eSetTypeException; + + Breakpoint *bp = NULL; + FileSpec module_spec; + const bool internal = false; + + 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 = target->CreateBreakpoint (&(m_options.m_modules), + file, + m_options.m_line_num, + check_inlines, + m_options.m_skip_prologue, + internal).get(); + } + break; + + case eSetTypeAddress: // Breakpoint by address + bp = target->CreateBreakpoint (m_options.m_load_addr, false).get(); + break; + + case eSetTypeFunctionName: // Breakpoint by function name + { + uint32_t name_type_mask = m_options.m_func_name_type_mask; + + if (name_type_mask == 0) + name_type_mask = eFunctionNameTypeAuto; + + bp = target->CreateBreakpoint (&(m_options.m_modules), + &(m_options.m_filenames), + m_options.m_func_names, + name_type_mask, + m_options.m_skip_prologue, + internal).get(); + } + break; + + case eSetTypeFunctionRegexp: // Breakpoint by regular expression function name + { + RegularExpression regexp(m_options.m_func_regexp.c_str()); + 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 = target->CreateFuncRegexBreakpoint (&(m_options.m_modules), + &(m_options.m_filenames), + regexp, + m_options.m_skip_prologue, + internal).get(); + } + break; + case eSetTypeSourceRegexp: // Breakpoint by regexp on source text. + { + const size_t num_files = m_options.m_filenames.GetSize(); + + if (num_files == 0) + { + 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.c_str()); + 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 = target->CreateSourceRegexBreakpoint (&(m_options.m_modules), &(m_options.m_filenames), regexp).get(); + } + break; + case eSetTypeException: + { + bp = target->CreateExceptionBreakpoint (m_options.m_language, m_options.m_catch_bp, m_options.m_throw_bp).get(); + } + break; + default: + break; + } + + // Now set the various options that were passed in: + if (bp) + { + if (m_options.m_thread_id != LLDB_INVALID_THREAD_ID) + bp->SetThreadID (m_options.m_thread_id); + + if (m_options.m_thread_index != UINT32_MAX) + bp->GetOptions()->GetThreadSpec()->SetIndex(m_options.m_thread_index); + + if (!m_options.m_thread_name.empty()) + bp->GetOptions()->GetThreadSpec()->SetName(m_options.m_thread_name.c_str()); + + if (!m_options.m_queue_name.empty()) + bp->GetOptions()->GetThreadSpec()->SetQueueName(m_options.m_queue_name.c_str()); + + if (m_options.m_ignore_count != 0) + bp->GetOptions()->SetIgnoreCount(m_options.m_ignore_count); + + if (!m_options.m_condition.empty()) + bp->GetOptions()->SetCondition(m_options.m_condition.c_str()); + + bp->SetOneShot (m_options.m_one_shot); + } + + if (bp) + { + Stream &output_stream = result.GetOutputStream(); + const bool show_locations = false; + bp->GetDescription(&output_stream, lldb::eDescriptionLevelInitial, show_locations); + // 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->GetNumLocations() == 0 && break_type != eSetTypeException) + output_stream.Printf ("WARNING: Unable to resolve breakpoint to any actual locations.\n"); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else if (!bp) + { + 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 == NULL) + { + 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; + } + + CommandOptions m_options; +}; +// 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_FILE ( LLDB_OPT_SET_FROM_TO(1, 9) & ~LLDB_OPT_SET_2 ) +#define LLDB_OPT_NOT_10 ( LLDB_OPT_SET_FROM_TO(1, 10) & ~LLDB_OPT_SET_10 ) +#define LLDB_OPT_SKIP_PROLOGUE ( LLDB_OPT_SET_1 | LLDB_OPT_SET_FROM_TO(3,8) ) + +OptionDefinition +CommandObjectBreakpointSet::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_NOT_10, false, "shlib", 's', required_argument, NULL, 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, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, + "Set the number of times this breakpoint is skipped before stopping." }, + + { LLDB_OPT_SET_ALL, false, "one-shot", 'o', no_argument, NULL, 0, eArgTypeNone, + "The breakpoint is deleted the first time it causes a stop." }, + + { LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, 0, eArgTypeExpression, + "The breakpoint stops only if this condition expression evaluates to true."}, + + { LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, 0, eArgTypeThreadIndex, + "The breakpoint stops only for the thread whose indeX matches this argument."}, + + { LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, 0, eArgTypeThreadID, + "The breakpoint stops only for the thread whose TID matches this argument."}, + + { LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, 0, eArgTypeThreadName, + "The breakpoint stops only for the thread whose thread name matches this argument."}, + + { LLDB_OPT_SET_ALL, false, "queue-name", 'q', required_argument, NULL, 0, eArgTypeQueueName, + "The breakpoint stops only for threads in the queue whose name is given by this argument."}, + + { LLDB_OPT_FILE, false, "file", 'f', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, "", + // "Set the breakpoint by source location at this particular column."}, + + { LLDB_OPT_SET_2, true, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, + "Set the breakpoint by address, at the specified address."}, + + { LLDB_OPT_SET_3, true, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Set the breakpoint by function name. Can be repeated multiple times to make one breakpoint for multiple names" }, + + { LLDB_OPT_SET_4, true, "fullname", 'F', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, 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\"" }, + + { LLDB_OPT_SET_10, true, "language-exception", 'E', required_argument, NULL, 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', required_argument, NULL, 0, eArgTypeBoolean, + "Set the breakpoint on exception throW." }, + + { LLDB_OPT_SET_10, false, "on-catch", 'h', required_argument, NULL, 0, eArgTypeBoolean, + "Set the breakpoint on exception catcH." }, + + { LLDB_OPT_SKIP_PROLOGUE, false, "skip-prologue", 'K', required_argument, NULL, 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." }, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// 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.", + NULL), + m_options (interpreter) + { + 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); + } + + + virtual + ~CommandObjectBreakpointModify () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_ignore_count (0), + m_thread_id(LLDB_INVALID_THREAD_ID), + m_thread_id_passed(false), + m_thread_index (UINT32_MAX), + m_thread_index_passed(false), + m_thread_name(), + m_queue_name(), + m_condition (), + m_one_shot (false), + m_enable_passed (false), + m_enable_value (false), + m_name_passed (false), + m_queue_passed (false), + m_condition_passed (false), + m_one_shot_passed (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + if (option_arg != NULL) + m_condition.assign (option_arg); + else + m_condition.clear(); + m_condition_passed = true; + break; + case 'd': + m_enable_passed = true; + m_enable_value = false; + break; + case 'e': + m_enable_passed = true; + m_enable_value = true; + break; + case 'i': + { + m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_ignore_count == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg); + } + break; + case 'o': + { + bool value, success; + value = Args::StringToBoolean(option_arg, false, &success); + if (success) + { + m_one_shot_passed = true; + m_one_shot = value; + } + else + error.SetErrorStringWithFormat("invalid boolean value '%s' passed for -o option", option_arg); + } + break; + case 't' : + { + if (option_arg[0] == '\0') + { + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_id_passed = true; + } + else + { + m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); + if (m_thread_id == LLDB_INVALID_THREAD_ID) + error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); + else + m_thread_id_passed = true; + } + } + break; + case 'T': + if (option_arg != NULL) + m_thread_name.assign (option_arg); + else + m_thread_name.clear(); + m_name_passed = true; + break; + case 'q': + if (option_arg != NULL) + m_queue_name.assign (option_arg); + else + m_queue_name.clear(); + m_queue_passed = true; + break; + case 'x': + { + if (option_arg[0] == '\n') + { + m_thread_index = UINT32_MAX; + m_thread_index_passed = true; + } + else + { + m_thread_index = Args::StringToUInt32 (option_arg, UINT32_MAX, 0); + if (m_thread_id == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); + else + m_thread_index_passed = true; + } + } + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + void + OptionParsingStarting () + { + m_ignore_count = 0; + m_thread_id = LLDB_INVALID_THREAD_ID; + m_thread_id_passed = false; + m_thread_index = UINT32_MAX; + m_thread_index_passed = false; + m_thread_name.clear(); + m_queue_name.clear(); + m_condition.clear(); + m_one_shot = false; + m_enable_passed = false; + m_queue_passed = false; + m_name_passed = false; + m_condition_passed = false; + m_one_shot_passed = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + uint32_t m_ignore_count; + lldb::tid_t m_thread_id; + bool m_thread_id_passed; + uint32_t m_thread_index; + bool m_thread_index_passed; + std::string m_thread_name; + std::string m_queue_name; + std::string m_condition; + bool m_one_shot; + bool m_enable_passed; + bool m_enable_value; + bool m_name_passed; + bool m_queue_passed; + bool m_condition_passed; + bool m_one_shot_passed; + + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + BreakpointIDList valid_bp_ids; + + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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) + { + if (m_options.m_thread_id_passed) + location->SetThreadID (m_options.m_thread_id); + + if (m_options.m_thread_index_passed) + location->SetThreadIndex(m_options.m_thread_index); + + if (m_options.m_name_passed) + location->SetThreadName(m_options.m_thread_name.c_str()); + + if (m_options.m_queue_passed) + location->SetQueueName(m_options.m_queue_name.c_str()); + + if (m_options.m_ignore_count != 0) + location->SetIgnoreCount(m_options.m_ignore_count); + + if (m_options.m_enable_passed) + location->SetEnabled (m_options.m_enable_value); + + if (m_options.m_condition_passed) + location->SetCondition (m_options.m_condition.c_str()); + } + } + else + { + if (m_options.m_thread_id_passed) + bp->SetThreadID (m_options.m_thread_id); + + if (m_options.m_thread_index_passed) + bp->SetThreadIndex(m_options.m_thread_index); + + if (m_options.m_name_passed) + bp->SetThreadName(m_options.m_thread_name.c_str()); + + if (m_options.m_queue_passed) + bp->SetQueueName(m_options.m_queue_name.c_str()); + + if (m_options.m_ignore_count != 0) + bp->SetIgnoreCount(m_options.m_ignore_count); + + if (m_options.m_enable_passed) + bp->SetEnabled (m_options.m_enable_value); + + if (m_options.m_condition_passed) + bp->SetCondition (m_options.m_condition.c_str()); + } + } + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark Modify::CommandOptions +OptionDefinition +CommandObjectBreakpointModify::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, "Set the number of times this breakpoint is skipped before stopping." }, +{ LLDB_OPT_SET_ALL, false, "one-shot", 'o', required_argument, NULL, 0, eArgTypeBoolean, "The breakpoint is deleted the first time it stop causes a stop." }, +{ LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, 0, eArgTypeThreadIndex, "The breakpoint stops only for the thread whose index matches this argument."}, +{ LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, 0, eArgTypeThreadID, "The breakpoint stops only for the thread whose TID matches this argument."}, +{ LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, 0, eArgTypeThreadName, "The breakpoint stops only for the thread whose thread name matches this argument."}, +{ LLDB_OPT_SET_ALL, false, "queue-name", 'q', required_argument, NULL, 0, eArgTypeQueueName, "The breakpoint stops only for threads in the queue whose name is given by this argument."}, +{ LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, 0, eArgTypeExpression, "The breakpoint stops only if this condition expression evaluates to true."}, +{ LLDB_OPT_SET_1, false, "enable", 'e', no_argument, NULL, 0, eArgTypeNone, "Enable the breakpoint."}, +{ LLDB_OPT_SET_2, false, "disable", 'd', no_argument, NULL, 0, eArgTypeNone, "Disable the breakpoint."}, +{ 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// 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.", + NULL) + { + 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); + } + + + virtual + ~CommandObjectBreakpointEnable () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + 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.GetArgumentCount() == 0) + { + // No breakpoint selected; enable all currently set breakpoints. + target->EnableAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints enabled. (%lu breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; enable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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 removing it/them. If no breakpoints are specified, disable them all.", + NULL) + { + SetHelpLong( +"Disable the specified breakpoint(s) without removing it/them. \n\ +If no breakpoints are specified, disable them all.\n\ +\n\ +Note: disabling a breakpoint will cause none of its locations to be hit\n\ +regardless of whether they are enabled or disabled. So the sequence: \n\ +\n\ + (lldb) break disable 1\n\ + (lldb) break enable 1.1\n\ +\n\ +will NOT cause location 1.1 to get hit. To achieve that, do:\n\ +\n\ + (lldb) break disable 1.*\n\ + (lldb) break enable 1.1\n\ +\n\ +The first command disables all the locations of breakpoint 1, \n\ +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); + + } + + + virtual + ~CommandObjectBreakpointDisable () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + 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.GetArgumentCount() == 0) + { + // No breakpoint selected; disable all currently set breakpoints. + target->DisableAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints disabled. (%lu breakpoints)\n", num_breakpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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 + +class CommandObjectBreakpointList : public CommandObjectParsed +{ +public: + CommandObjectBreakpointList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint list", + "List some or all breakpoints at configurable levels of detail.", + NULL), + m_options (interpreter) + { + 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); + } + + + virtual + ~CommandObjectBreakpointList () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_level (lldb::eDescriptionLevelBrief) // Breakpoint List defaults to brief descriptions + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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; + case 'i': + m_internal = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_level = lldb::eDescriptionLevelFull; + m_internal = false; + } + + const OptionDefinition * + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + lldb::DescriptionLevel m_level; + + bool m_internal; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No current target or breakpoints."); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + + const BreakpointList &breakpoints = target->GetBreakpointList(m_options.m_internal); + Mutex::Locker locker; + target->GetBreakpointList(m_options.m_internal).GetListMutex(locker); + + 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.GetArgumentCount() == 0) + { + // 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(); + 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::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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; +}; + +#pragma mark List::CommandOptions +OptionDefinition +CommandObjectBreakpointList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "internal", 'i', no_argument, NULL, 0, eArgTypeNone, + "Show debugger internal breakpoints" }, + + { LLDB_OPT_SET_1, false, "brief", 'b', no_argument, NULL, 0, eArgTypeNone, + "Give a brief description of the breakpoint (no location info)."}, + + // 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. + { LLDB_OPT_SET_2, false, "full", 'f', no_argument, NULL, 0, eArgTypeNone, + "Give a full description of the breakpoint and its locations."}, + + { LLDB_OPT_SET_3, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, + "Explain everything we know about the breakpoint (for debugging debugger bugs)." }, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointClear +//------------------------------------------------------------------------- +#pragma mark Clear + +class CommandObjectBreakpointClear : public CommandObjectParsed +{ +public: + + typedef enum BreakpointClearType + { + eClearTypeInvalid, + eClearTypeFileAndLine + } BreakpointClearType; + + CommandObjectBreakpointClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "breakpoint clear", + "Clears a breakpoint or set of breakpoints in the executable.", + "breakpoint clear "), + m_options (interpreter) + { + } + + virtual + ~CommandObjectBreakpointClear () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_filename (), + m_line_num (0) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': + m_filename.assign (option_arg); + break; + + case 'l': + m_line_num = Args::StringToUInt32 (option_arg, 0); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_filename.clear(); + m_line_num = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_filename; + uint32_t m_line_num; + + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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; + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + 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 BreakIDs; + for (size_t i = 0; i < num_breakpoints; ++i) + BreakIDs.push_back(breakpoints.GetBreakpointAtIndex(i).get()->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.GetData(); + output_stream.EOL(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("Breakpoint clear: No breakpoint cleared."); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; +}; + +#pragma mark Clear::CommandOptions + +OptionDefinition +CommandObjectBreakpointClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, + "Specify the breakpoint by source location in this particular file."}, + + { LLDB_OPT_SET_1, true, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, + "Specify the breakpoint by source location at this particular line."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointDelete +//------------------------------------------------------------------------- +#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.", + NULL) + { + 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); + } + + virtual + ~CommandObjectBreakpointDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + result.AppendError ("Invalid target. No existing target or breakpoints."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Mutex::Locker locker; + target->GetBreakpointList().GetListMutex(locker); + + 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.GetArgumentCount() == 0) + { + if (!m_interpreter.Confirm ("About to delete all breakpoints, do you want to do that?", true)) + { + result.AppendMessage("Operation cancelled..."); + } + else + { + target->RemoveAllBreakpoints (); + result.AppendMessageWithFormat ("All breakpoints removed. (%lu %s)\n", num_breakpoints, num_breakpoints > 1 ? "breakpoints" : "breakpoint"); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular breakpoint selected; disable that breakpoint. + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- +#pragma mark MultiwordBreakpoint + +CommandObjectMultiwordBreakpoint::CommandObjectMultiwordBreakpoint (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "breakpoint", + "A set of commands for operating on breakpoints. Also see _regexp-break.", + "breakpoint []") +{ + 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)); + + 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"); + + 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); +} + +CommandObjectMultiwordBreakpoint::~CommandObjectMultiwordBreakpoint () +{ +} + +void +CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, + BreakpointIDList *valid_ids) +{ + // 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. + // If args is empty, we will use the last created breakpoint (if there is one.) + + Args temp_args; + + if (args.GetArgumentCount() == 0) + { + 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, result, temp_args); + + // NOW, convert the list of breakpoint id strings in TEMP_ARGS into an actual BreakpointIDList: + + valid_ids->InsertStringArray (temp_args.GetConstArgumentVector(), temp_args.GetArgumentCount(), 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 != NULL) + { + const size_t num_locations = breakpoint->GetNumLocations(); + if (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/source/Commands/CommandObjectBreakpoint.h b/source/Commands/CommandObjectBreakpoint.h new file mode 100644 index 00000000000..2d674b22d70 --- /dev/null +++ b/source/Commands/CommandObjectBreakpoint.h @@ -0,0 +1,47 @@ +//===-- CommandObjectBreakpoint.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectBreakpoint_h_ +#define liblldb_CommandObjectBreakpoint_h_ + +// C Includes +// C++ Includes + +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Address.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- + +class CommandObjectMultiwordBreakpoint : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordBreakpoint (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordBreakpoint (); + + static void + VerifyBreakpointIDs (Args &args, Target *target, CommandReturnObject &result, BreakpointIDList *valid_ids); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectBreakpoint_h_ diff --git a/source/Commands/CommandObjectBreakpointCommand.cpp b/source/Commands/CommandObjectBreakpointCommand.cpp new file mode 100644 index 00000000000..c4504a4c651 --- /dev/null +++ b/source/Commands/CommandObjectBreakpointCommand.cpp @@ -0,0 +1,915 @@ +//===-- CommandObjectBreakpointCommand.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes + + +#include "CommandObjectBreakpointCommand.h" +#include "CommandObjectBreakpoint.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Breakpoint/BreakpointIDList.h" +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/State.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandAdd +//------------------------------------------------------------------------- + + +class CommandObjectBreakpointCommandAdd : public CommandObjectParsed +{ +public: + + CommandObjectBreakpointCommandAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "add", + "Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit.", + NULL), + m_options (interpreter) + { + SetHelpLong ( +"\nGeneral information about entering breakpoint commands\n\ +------------------------------------------------------\n\ +\n\ +This command will cause you to be prompted to enter the command or set of\n\ +commands you wish to be executed when the specified breakpoint is hit. You\n\ +will be told to enter your command(s), and will see a '> 'prompt. Because\n\ +you can enter one or many commands to be executed when a breakpoint is hit,\n\ +you will continue to be prompted after each new-line that you enter, until you\n\ +enter the word 'DONE', which will cause the commands you have entered to be\n\ +stored with the breakpoint and executed when the breakpoint is hit.\n\ +\n\ +Syntax checking is not necessarily done when breakpoint commands are entered.\n\ +An improperly written breakpoint command will attempt to get executed when the\n\ +breakpoint gets hit, and usually silently fail. If your breakpoint command does\n\ +not appear to be getting executed, go back and check your syntax.\n\ +\n\ +Special information about PYTHON breakpoint commands\n\ +----------------------------------------------------\n\ +\n\ +You may enter either one line of Python, multiple lines of Python (including\n\ +function definitions), or specify a Python function in a module that has already,\n\ +or will be imported. If you enter a single line of Python, that will be passed\n\ +to the Python interpreter 'as is' when the breakpoint gets hit. If you enter\n\ +function definitions, they will be passed to the Python interpreter as soon as\n\ +you finish entering the breakpoint command, and they can be called later (don't\n\ +forget to add calls to them, if you want them called when the breakpoint is\n\ +hit). If you enter multiple lines of Python that are not function definitions,\n\ +they will be collected into a new, automatically generated Python function, and\n\ +a call to the newly generated function will be attached to the breakpoint.\n\ +\n\ +\n\ +This auto-generated function is passed in three arguments:\n\ +\n\ + frame: a lldb.SBFrame object for the frame which hit breakpoint.\n\ + bp_loc: a lldb.SBBreakpointLocation object that represents the breakpoint\n\ + location that was hit.\n\ + dict: the python session dictionary hit.\n\ +\n\ +When specifying a python function with the --python-function option, you need\n\ +to supply the function name prepended by the module name. So if you import a\n\ +module named 'myutils' that contains a 'breakpoint_callback' function, you would\n\ +specify the option as:\n\ +\n\ + --python-function myutils.breakpoint_callback\n\ +\n\ +The function itself must have the following prototype:\n\ +\n\ +def breakpoint_callback(frame, bp_loc, dict):\n\ + # Your code goes here\n\ +\n\ +The arguments are the same as the 3 auto generation function arguments listed\n\ +above. Note that the global variable 'lldb.frame' will NOT be setup when this\n\ +function is called, so be sure to use the 'frame' argument. The 'frame' argument\n\ +can get you to the thread (frame.GetThread()), the thread can get you to the\n\ +process (thread.GetProcess()), and the process can get you back to the target\n\ +(process.GetTarget()).\n\ +\n\ +Important Note: Because loose Python code gets collected into functions, if you\n\ +want to access global variables in the 'loose' code, you need to specify that\n\ +they are global, using the 'global' keyword. Be sure to use correct Python\n\ +syntax, including indentation, when entering Python breakpoint commands.\n\ +\n\ +As a third option, you can pass the name of an already existing Python function\n\ +and that function will be attached to the breakpoint. It will get passed the\n\ +frame and bp_loc arguments mentioned above.\n\ +\n\ +Example Python one-line breakpoint command:\n\ +\n\ +(lldb) breakpoint command add -s python 1\n\ +Enter your Python command(s). Type 'DONE' to end.\n\ +> print \"Hit this breakpoint!\"\n\ +> DONE\n\ +\n\ +As a convenience, this also works for a short Python one-liner:\n\ +(lldb) breakpoint command add -s python 1 -o \"import time; print time.asctime()\"\n\ +(lldb) run\n\ +Launching '.../a.out' (x86_64)\n\ +(lldb) Fri Sep 10 12:17:45 2010\n\ +Process 21778 Stopped\n\ +* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread\n\ + 36 \n\ + 37 int c(int val)\n\ + 38 {\n\ + 39 -> return val + 3;\n\ + 40 }\n\ + 41 \n\ + 42 int main (int argc, char const *argv[])\n\ +(lldb)\n\ +\n\ +Example multiple line Python breakpoint command, using function definition:\n\ +\n\ +(lldb) breakpoint command add -s python 1\n\ +Enter your Python command(s). Type 'DONE' to end.\n\ +> def breakpoint_output (bp_no):\n\ +> out_string = \"Hit breakpoint number \" + repr (bp_no)\n\ +> print out_string\n\ +> return True\n\ +> breakpoint_output (1)\n\ +> DONE\n\ +\n\ +\n\ +Example multiple line Python breakpoint command, using 'loose' Python:\n\ +\n\ +(lldb) breakpoint command add -s p 1\n\ +Enter your Python command(s). Type 'DONE' to end.\n\ +> global bp_count\n\ +> bp_count = bp_count + 1\n\ +> print \"Hit this breakpoint \" + repr(bp_count) + \" times!\"\n\ +> DONE\n\ +\n\ +In this case, since there is a reference to a global variable,\n\ +'bp_count', you will also need to make sure 'bp_count' exists and is\n\ +initialized:\n\ +\n\ +(lldb) script\n\ +>>> bp_count = 0\n\ +>>> quit()\n\ +\n\ +(lldb)\n\ +\n\ +\n\ +Your Python code, however organized, can optionally return a value.\n\ +If the returned value is False, that tells LLDB not to stop at the breakpoint\n\ +to which the code is associated. Returning anything other than False, or even\n\ +returning None, or even omitting a return statement entirely, will cause\n\ +LLDB to stop.\n\ +\n\ +Final Note: If you get a warning that no breakpoint command was generated, but\n\ +you did not get any syntax errors, you probably forgot to add a call to your\n\ +functions.\n\ +\n\ +Special information about debugger command breakpoint commands\n\ +--------------------------------------------------------------\n\ +\n\ +You may enter any debugger command, exactly as you would at the debugger prompt.\n\ +You may enter as many debugger commands as you like, but do NOT enter more than\n\ +one command per line.\n" ); + + 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); + } + + virtual + ~CommandObjectBreakpointCommandAdd () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) + { + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + std::unique_ptr data_ap(new BreakpointOptions::CommandData()); + if (reader_sp && data_ap.get()) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); + + Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback, + bp_options, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + "DONE", // end token + "> ", // prompt + true)); // echo input + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + + } + + /// Set a one-liner as the callback for the breakpoint. + void + SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner) + { + std::unique_ptr data_ap(new BreakpointOptions::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 breakpoint command list) + // while the latter is used for Python to interpret during the actual callback. + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + data_ap->stop_on_error = m_options.m_stop_on_error; + + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (BreakpointOptionsCallbackFunction, baton_sp); + + return; + } + + static size_t + GenerateBreakpointCommandCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len && baton) + { + BreakpointOptions *bp_options = (BreakpointOptions *) baton; + if (bp_options) + { + Baton *bp_options_baton = bp_options->GetBaton(); + if (bp_options_baton) + ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); + } + } + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderInterrupt: + { + // Finish, and cancel the breakpoint command. + reader.SetIsDone (true); + BreakpointOptions *bp_options = (BreakpointOptions *) baton; + if (bp_options) + { + Baton *bp_options_baton = bp_options->GetBaton (); + if (bp_options_baton) + { + ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->user_source.Clear(); + ((BreakpointOptions::CommandData *) bp_options_baton->m_data)->script_source.clear(); + } + } + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; + } + + static bool + BreakpointOptionsCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id) + { + bool ret_value = true; + if (baton == NULL) + return true; + + + BreakpointOptions::CommandData *data = (BreakpointOptions::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); + + bool stop_on_continue = true; + bool echo_commands = false; + bool print_results = true; + + debugger.GetCommandInterpreter().HandleCommands (commands, + &exe_ctx, + stop_on_continue, + data->stop_on_error, + echo_commands, + print_results, + eLazyBoolNo, + result); + result.GetImmediateOutputStream()->Flush(); + result.GetImmediateErrorStream()->Flush(); + } + } + return ret_value; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_use_commands (false), + m_use_script_language (false), + m_script_language (eScriptLanguageNone), + m_use_one_liner (false), + m_one_liner(), + m_function_name() + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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) Args::StringToOptionEnum (option_arg, + g_option_table[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 = Args::StringToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for stop-on-error: \"%s\"", option_arg); + } + 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 () + { + 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(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + 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 == false && m_options.m_function_name.size()) + { + 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::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + if (result.Succeeded()) + { + const size_t count = valid_bp_ids.GetSize(); + if (count > 1) + { + result.AppendError ("can only add commands to one breakpoint at a time."); + result.SetStatus (eReturnStatusFailed); + return false; + } + + 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 = NULL; + 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(); + } + + // Skip this breakpoint if bp_options is not good. + if (bp_options == NULL) 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) + { + m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options, + m_options.m_one_liner.c_str()); + } + // Special handling for using a Python function by name + // instead of extending the breakpoint callback data structures, we just automatize + // what the user would do manually: make their breakpoint command be a function call + else if (m_options.m_function_name.size()) + { + std::string oneliner("return "); + oneliner += m_options.m_function_name; + oneliner += "(frame, bp_loc, internal_dict)"; + m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options, + oneliner.c_str()); + } + else + { + m_interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp_options, + result); + } + } + else + { + // Special handling for one-liner specified inline. + if (m_options.m_use_one_liner) + SetBreakpointCommandCallback (bp_options, + m_options.m_one_liner.c_str()); + else + CollectDataForBreakpointCommandCallback (bp_options, + result); + } + } + } + } + + return result.Succeeded(); + } + +private: + CommandOptions m_options; + static const char *g_reader_instructions; + +}; + +const char * +CommandObjectBreakpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; + +// 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 OptionEnumValueElement +g_script_option_enumeration[4] = +{ + { 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."}, + { 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectBreakpointCommandAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "one-liner", 'o', required_argument, NULL, 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', required_argument, NULL, 0, eArgTypeBoolean, + "Specify whether breakpoint command execution should terminate on error." }, + + { LLDB_OPT_SET_ALL, false, "script-type", 's', required_argument, g_script_option_enumeration, 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', required_argument, NULL, 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."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectBreakpointCommandDelete +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommandDelete : public CommandObjectParsed +{ +public: + CommandObjectBreakpointCommandDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "delete", + "Delete the set of commands from a breakpoint.", + NULL) + { + 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); + } + + + virtual + ~CommandObjectBreakpointCommandDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + 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.GetArgumentCount() == 0) + { + result.AppendError ("No breakpoint specified from which to delete the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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(); + } +}; + +//------------------------------------------------------------------------- +// 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.", + NULL) + { + 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); + } + + virtual + ~CommandObjectBreakpointCommandList () {} + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + 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.GetArgumentCount() == 0) + { + result.AppendError ("No breakpoint specified for which to list the commands"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + BreakpointIDList valid_bp_ids; + CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids); + + 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) + { + const BreakpointOptions *bp_options = NULL; + if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) + { + BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID())); + if (bp_loc_sp) + bp_options = bp_loc_sp->GetOptionsNoCreate(); + else + { + result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n", + cur_bp_id.GetBreakpointID(), + cur_bp_id.GetLocationID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + bp_options = bp->GetOptions(); + } + + if (bp_options) + { + StreamString id_str; + BreakpointID::GetCanonicalReference (&id_str, + cur_bp_id.GetBreakpointID(), + cur_bp_id.GetLocationID()); + const Baton *baton = bp_options->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", + "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').", + "command [] ") +{ + 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 () +{ +} + + diff --git a/source/Commands/CommandObjectBreakpointCommand.h b/source/Commands/CommandObjectBreakpointCommand.h new file mode 100644 index 00000000000..afedb7602cd --- /dev/null +++ b/source/Commands/CommandObjectBreakpointCommand.h @@ -0,0 +1,46 @@ +//===-- CommandObjectBreakpointCommand.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectBreakpointCommand_h_ +#define liblldb_CommandObjectBreakpointCommand_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordBreakpoint +//------------------------------------------------------------------------- + +class CommandObjectBreakpointCommand : public CommandObjectMultiword +{ +public: + CommandObjectBreakpointCommand (CommandInterpreter &interpreter); + + virtual + ~CommandObjectBreakpointCommand (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectBreakpointCommand_h_ diff --git a/source/Commands/CommandObjectCommands.cpp b/source/Commands/CommandObjectCommands.cpp new file mode 100644 index 00000000000..4699aa6d9c4 --- /dev/null +++ b/source/Commands/CommandObjectCommands.cpp @@ -0,0 +1,2021 @@ +//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectCommands.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/StringRef.h" + +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandHistory.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObjectRegexCommand.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionValueBoolean.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectCommandsSource +//------------------------------------------------------------------------- + +class CommandObjectCommandsHistory : public CommandObjectParsed +{ +public: + CommandObjectCommandsHistory(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command history", + "Dump the history of commands in this session.", + NULL), + m_options (interpreter) + { + } + + ~CommandObjectCommandsHistory () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_start_idx(0), + m_stop_idx(0), + m_count(0), + m_clear(false) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + error = m_count.SetValueFromCString(option_arg,eVarSetOperationAssign); + break; + case 's': + if (option_arg && strcmp("end", option_arg) == 0) + { + m_start_idx.SetCurrentValue(UINT64_MAX); + m_start_idx.SetOptionWasSet(); + } + else + error = m_start_idx.SetValueFromCString(option_arg,eVarSetOperationAssign); + break; + case 'e': + error = m_stop_idx.SetValueFromCString(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 () + { + m_start_idx.Clear(); + m_stop_idx.Clear(); + m_count.Clear(); + m_clear.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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) + { + 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 start_idx = {m_options.m_start_idx.OptionWasSet(),m_options.m_start_idx.GetCurrentValue()}; + std::pair stop_idx = {m_options.m_stop_idx.OptionWasSet(),m_options.m_stop_idx.GetCurrentValue()}; + std::pair 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; +}; + +OptionDefinition +CommandObjectCommandsHistory::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "count", 'c', required_argument, NULL, 0, eArgTypeUnsignedInteger, "How many history commands to print."}, +{ LLDB_OPT_SET_1, false, "start-index", 's', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Index at which to start printing history commands (or end to mean tail mode)."}, +{ LLDB_OPT_SET_1, false, "end-index", 'e', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Index at which to stop printing history commands."}, +{ LLDB_OPT_SET_2, false, "clear", 'C', no_argument, NULL, 0, eArgTypeBoolean, "Clears the current command history."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectCommandsSource +//------------------------------------------------------------------------- + +class CommandObjectCommandsSource : public CommandObjectParsed +{ +public: + CommandObjectCommandsSource(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command source", + "Read in debugger commands from the file and execute them.", + NULL), + m_options (interpreter) + { + 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 () {} + + virtual const char* + GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return ""; + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_stop_on_error (true) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'e': + error = m_stop_on_error.SetValueFromCString(option_arg); + break; + case 'c': + m_stop_on_continue = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for stop-on-continue: %s", option_arg); + break; + case 's': + m_silent_run = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for silent-run: %s", option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_stop_on_error.Clear(); + m_silent_run = false; + m_stop_on_continue = true; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + OptionValueBoolean m_stop_on_error; + bool m_silent_run; + bool m_stop_on_continue; + }; + + bool + DoExecute(Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 1) + { + const char *filename = command.GetArgumentAtIndex(0); + + result.AppendMessageWithFormat ("Executing commands in '%s'.\n", filename); + + FileSpec cmd_file (filename, true); + ExecutionContext *exe_ctx = NULL; // Just use the default context. + bool echo_commands = !m_options.m_silent_run; + bool print_results = true; + bool stop_on_error = m_options.m_stop_on_error.OptionWasSet() ? (bool)m_options.m_stop_on_error : m_interpreter.GetStopCmdSourceOnError(); + + m_interpreter.HandleCommandsFromFile (cmd_file, + exe_ctx, + m_options.m_stop_on_continue, + stop_on_error, + echo_commands, + print_results, + eLazyBoolCalculate, + result); + } + else + { + result.AppendErrorWithFormat("'%s' takes exactly one executable filename argument.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + + } + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectCommandsSource::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', required_argument, NULL, 0, eArgTypeBoolean, "If true, stop executing commands on error."}, +{ LLDB_OPT_SET_ALL, false, "stop-on-continue", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, stop executing commands on continue."}, +{ LLDB_OPT_SET_ALL, false, "silent-run", 's', required_argument, NULL, 0, eArgTypeBoolean, "If true don't echo commands while executing."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectCommandsAlias +//------------------------------------------------------------------------- +// CommandObjectCommandsAlias +//------------------------------------------------------------------------- + +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):"; + + +class CommandObjectCommandsAlias : public CommandObjectRaw +{ + + +public: + CommandObjectCommandsAlias (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "command alias", + "Allow users to define their own debugger command abbreviations.", + NULL) + { + SetHelpLong( + "'alias' allows the user to create a short-cut or abbreviation for long \n\ + commands, multi-word commands, and commands that take particular options. \n\ + Below are some simple examples of how one might use the 'alias' command: \n\ + \n 'command alias sc script' // Creates the abbreviation 'sc' for the 'script' \n\ + // command. \n\ + 'command alias bp breakpoint' // Creates the abbreviation 'bp' for the 'breakpoint' \n\ + // command. Since breakpoint commands are two-word \n\ + // commands, the user will still need to enter the \n\ + // second word after 'bp', e.g. 'bp enable' or \n\ + // 'bp delete'. \n\ + 'command alias bpl breakpoint list' // Creates the abbreviation 'bpl' for the \n\ + // two-word command 'breakpoint list'. \n\ + \nAn alias can include some options for the command, with the values either \n\ + filled in at the time the alias is created, or specified as positional \n\ + arguments, to be filled in when the alias is invoked. The following example \n\ + shows how to create aliases with options: \n\ + \n\ + 'command alias bfl breakpoint set -f %1 -l %2' \n\ + \nThis creates the abbreviation 'bfl' (for break-file-line), with the -f and -l \n\ + options already part of the alias. So if the user wants to set a breakpoint \n\ + by file and line without explicitly having to use the -f and -l options, the \n\ + user can now use 'bfl' instead. The '%1' and '%2' are positional placeholders \n\ + for the actual arguments that will be passed when the alias command is used. \n\ + The number in the placeholder refers to the position/order the actual value \n\ + occupies when the alias is used. All the occurrences of '%1' in the alias \n\ + will be replaced with the first argument, all the occurrences of '%2' in the \n\ + alias will be replaced with the second argument, and so on. This also allows \n\ + actual arguments to be used multiple times within an alias (see 'process \n\ + launch' example below). \n\ + Note: the positional arguments must substitute as whole words in the resultant\n\ + command, so you can't at present do something like:\n\ + \n\ + command alias bcppfl breakpoint set -f %1.cpp -l %2\n\ + \n\ + to get the file extension \".cpp\" automatically appended. For more complex\n\ + aliasing, use the \"command regex\" command instead.\n\ + \nSo in the 'bfl' case, the actual file value will be \n\ + filled in with the first argument following 'bfl' and the actual line number \n\ + value will be filled in with the second argument. The user would use this \n\ + alias as follows: \n\ + \n (lldb) command alias bfl breakpoint set -f %1 -l %2 \n\ + <... some time later ...> \n\ + (lldb) bfl my-file.c 137 \n\ + \nThis would be the same as if the user had entered \n\ + 'breakpoint set -f my-file.c -l 137'. \n\ + \nAnother example: \n\ + \n (lldb) command alias pltty process launch -s -o %1 -e %1 \n\ + (lldb) pltty /dev/tty0 \n\ + // becomes 'process launch -s -o /dev/tty0 -e /dev/tty0' \n\ + \nIf the user always wanted to pass the same value to a particular option, the \n\ + alias could be defined with that value directly in the alias as a constant, \n\ + rather than using a positional placeholder: \n\ + \n command alias bl3 breakpoint set -f %1 -l 3 // Always sets a breakpoint on line \n\ + // 3 of whatever file is indicated. \n"); + + 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 () + { + } + +protected: + virtual bool + DoExecute (const char *raw_command_line, CommandReturnObject &result) + { + Args args (raw_command_line); + std::string raw_command_string (raw_command_line); + + size_t argc = args.GetArgumentCount(); + + if (argc < 2) + { + result.AppendError ("'alias' requires at least two arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Get the alias command. + + const std::string alias_command = args.GetArgumentAtIndex (0); + + // 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.c_str())) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", + alias_command.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. + CommandObject *cmd_obj = m_interpreter.GetCommandObjectForCommand (raw_command_string); + + if (!cmd_obj) + { + result.AppendErrorWithFormat ("invalid command given to 'alias'. '%s' does not begin with a valid command." + " No alias created.", raw_command_string.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 (const std::string &alias_command, std::string &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); + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact (cmd_obj.GetCommandName(), false); + + if (!m_interpreter.ProcessAliasOptionsArgs (cmd_obj_sp, raw_command_string.c_str(), option_arg_vector_sp)) + { + result.AppendError ("Unable to create requested alias.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + // Create the alias + if (m_interpreter.AliasExists (alias_command.c_str()) + || m_interpreter.UserCommandExists (alias_command.c_str())) + { + OptionArgVectorSP temp_option_arg_sp (m_interpreter.GetAliasOptions (alias_command.c_str())); + if (temp_option_arg_sp.get()) + { + if (option_arg_vector->size() == 0) + m_interpreter.RemoveAliasOptions (alias_command.c_str()); + } + result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", + alias_command.c_str()); + } + + if (cmd_obj_sp) + { + m_interpreter.AddAlias (alias_command.c_str(), cmd_obj_sp); + if (option_arg_vector->size() > 0) + m_interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + 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 ("'alias' requires at least two arguments"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const std::string alias_command = args.GetArgumentAtIndex(0); + const std::string actual_command = args.GetArgumentAtIndex(1); + + 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.c_str())) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n", + alias_command.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + CommandObjectSP command_obj_sp(m_interpreter.GetCommandSPExact (actual_command.c_str(), true)); + CommandObjectSP subcommand_obj_sp; + bool use_subcommand = false; + if (command_obj_sp.get()) + { + CommandObject *cmd_obj = command_obj_sp.get(); + CommandObject *sub_cmd_obj = NULL; + OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector); + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + while (cmd_obj->IsMultiwordObject() && args.GetArgumentCount() > 0) + { + if (argc >= 3) + { + const std::string sub_command = args.GetArgumentAtIndex(0); + assert (sub_command.length() != 0); + subcommand_obj_sp = cmd_obj->GetSubcommandSP (sub_command.c_str()); + if (subcommand_obj_sp.get()) + { + 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; + } + else + { + result.AppendErrorWithFormat("'%s' is not a valid sub-command of '%s'. " + "Unable to create alias.\n", + sub_command.c_str(), actual_command.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + // Verify & handle any options/arguments passed to the alias command + + if (args.GetArgumentCount () > 0) + { + CommandObjectSP tmp_sp = m_interpreter.GetCommandSPExact (cmd_obj->GetCommandName(), false); + if (use_subcommand) + tmp_sp = m_interpreter.GetCommandSPExact (sub_cmd_obj->GetCommandName(), false); + + std::string args_string; + args.GetCommandString (args_string); + + if (!m_interpreter.ProcessAliasOptionsArgs (tmp_sp, args_string.c_str(), option_arg_vector_sp)) + { + result.AppendError ("Unable to create requested alias.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + // Create the alias. + + if (m_interpreter.AliasExists (alias_command.c_str()) + || m_interpreter.UserCommandExists (alias_command.c_str())) + { + OptionArgVectorSP tmp_option_arg_sp (m_interpreter.GetAliasOptions (alias_command.c_str())); + if (tmp_option_arg_sp.get()) + { + if (option_arg_vector->size() == 0) + m_interpreter.RemoveAliasOptions (alias_command.c_str()); + } + result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n", + alias_command.c_str()); + } + + if (use_subcommand) + m_interpreter.AddAlias (alias_command.c_str(), subcommand_obj_sp); + else + m_interpreter.AddAlias (alias_command.c_str(), command_obj_sp); + if (option_arg_vector->size() > 0) + m_interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("'%s' is not an existing command.\n", actual_command.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + return result.Succeeded(); + } + +}; + +#pragma mark CommandObjectCommandsUnalias +//------------------------------------------------------------------------- +// CommandObjectCommandsUnalias +//------------------------------------------------------------------------- + +class CommandObjectCommandsUnalias : public CommandObjectParsed +{ +public: + CommandObjectCommandsUnalias (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command unalias", + "Allow the user to remove/delete a user-defined command abbreviation.", + NULL) + { + 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() + { + } + +protected: + bool + DoExecute (Args& args, CommandReturnObject &result) + { + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + + if (args.GetArgumentCount() != 0) + { + const char *command_name = args.GetArgumentAtIndex(0); + cmd_obj = m_interpreter.GetCommandObject(command_name); + if (cmd_obj) + { + if (m_interpreter.CommandExists (command_name)) + { + result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be removed.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + else + { + + if (m_interpreter.RemoveAlias (command_name) == false) + { + if (m_interpreter.AliasExists (command_name)) + result.AppendErrorWithFormat ("Error occurred while attempting to unalias '%s'.\n", + command_name); + else + result.AppendErrorWithFormat ("'%s' is not an existing alias.\n", command_name); + result.SetStatus (eReturnStatusFailed); + } + else + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + result.AppendErrorWithFormat ("'%s' is not a known command.\nTry 'help' to see a " + "current list of commands.\n", + command_name); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("must call 'unalias' with a valid alias"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsAddRegex +//------------------------------------------------------------------------- +#pragma mark CommandObjectCommandsAddRegex + +class CommandObjectCommandsAddRegex : public CommandObjectParsed +{ +public: + CommandObjectCommandsAddRegex (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command regex", + "Allow the user to create a regular expression command.", + "command regex [s/// ...]"), + m_options (interpreter) + { + SetHelpLong( +"This command allows the user to create powerful regular expression commands\n" +"with substitutions. The regular expressions and substitutions are specified\n" +"using the regular exression substitution format of:\n" +"\n" +" s///\n" +"\n" +" is a regular expression that can use parenthesis to capture regular\n" +"expression input and substitute the captured matches in the output using %1\n" +"for the first match, %2 for the second, and so on.\n" +"\n" +"The regular expressions can all be specified on the command line if more than\n" +"one argument is provided. If just the command name is provided on the command\n" +"line, then the regular expressions and substitutions can be entered on separate\n" +" lines, followed by an empty line to terminate the command definition.\n" +"\n" +"EXAMPLES\n" +"\n" +"The following example will define a regular expression command named 'f' that\n" +"will call 'finish' if there are no arguments, or 'frame select ' if\n" +"a number follows 'f':\n" +"\n" +" (lldb) command regex f s/^$/finish/ 's/([0-9]+)/frame select %1/'\n" +"\n" + ); + } + + ~CommandObjectCommandsAddRegex() + { + } + + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + result.AppendError ("usage: 'command regex [s/// s/// ...]'\n"); + result.SetStatus (eReturnStatusFailed); + } + else + { + Error error; + const char *name = command.GetArgumentAtIndex(0); + m_regex_cmd_ap.reset (new CommandObjectRegexCommand (m_interpreter, + name, + m_options.GetHelp (), + m_options.GetSyntax (), + 10)); + + if (argc == 1) + { + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (reader_sp) + { + error =reader_sp->Initialize (CommandObjectCommandsAddRegex::InputReaderCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + "> ", // prompt + true); // echo input + if (error.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + return true; + } + } + } + else + { + for (size_t arg_idx = 1; arg_idx < argc; ++arg_idx) + { + llvm::StringRef arg_strref (command.GetArgumentAtIndex(arg_idx)); + error = AppendRegexSubstitution (arg_strref); + if (error.Fail()) + break; + } + + if (error.Success()) + { + AddRegexCommandToInterpreter(); + } + } + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + + return result.Succeeded(); + } + + Error + AppendRegexSubstitution (const llvm::StringRef ®ex_sed) + { + Error error; + + if (m_regex_cmd_ap.get() == NULL) + { + 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///" or "s|||" + 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'", + separator_char, + (int)(regex_sed.size() - first_separator_char_pos - 1), + regex_sed.data() + (first_separator_char_pos + 1)); + 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'", + separator_char, + (int)(regex_sed.size() - second_separator_char_pos - 1), + regex_sed.data() + (second_separator_char_pos + 1)); + 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(" can't be empty in 's%c%c%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(" can't be empty in 's%c%c%c' string: '%.*s'", + separator_char, + separator_char, + separator_char, + (int)regex_sed.size(), + regex_sed.data()); + return error; + } + 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_ap->AddRegexCommand (regex.c_str(), + subst.c_str()); + return error; + } + + void + AddRegexCommandToInterpreter() + { + if (m_regex_cmd_ap.get()) + { + if (m_regex_cmd_ap->HasRegexEntries()) + { + CommandObjectSP cmd_sp (m_regex_cmd_ap.release()); + m_interpreter.AddCommand(cmd_sp->GetCommandName(), cmd_sp, true); + } + } + } + + void + InputReaderDidCancel() + { + m_regex_cmd_ap.reset(); + } + + static size_t + InputReaderCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + CommandObjectCommandsAddRegex *add_regex_cmd = (CommandObjectCommandsAddRegex *) baton; + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream (); + out_stream->Printf("%s\n", "Enter regular expressions in the form 's///' and terminate with an empty line:"); + out_stream->Flush(); + } + break; + case eInputReaderReactivate: + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + while (bytes_len > 0 && (bytes[bytes_len-1] == '\r' || bytes[bytes_len-1] == '\n')) + --bytes_len; + if (bytes_len == 0) + reader.SetIsDone(true); + else if (bytes) + { + llvm::StringRef bytes_strref (bytes, bytes_len); + Error error (add_regex_cmd->AppendRegexSubstitution (bytes_strref)); + if (error.Fail()) + { + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf("error: %s\n", error.AsCString()); + out_stream->Flush(); + } + add_regex_cmd->InputReaderDidCancel (); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderInterrupt: + { + reader.SetIsDone (true); + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->PutCString("Regular expression command creations was cancelled.\n"); + out_stream->Flush(); + } + add_regex_cmd->InputReaderDidCancel (); + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + add_regex_cmd->AddRegexCommandToInterpreter(); + break; + } + + return bytes_len; + } + +private: + std::unique_ptr m_regex_cmd_ap; + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + m_help.clear(); + m_syntax.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + const char * + GetHelp () + { + if (m_help.empty()) + return NULL; + return m_help.c_str(); + } + const char * + GetSyntax () + { + if (m_syntax.empty()) + return NULL; + return m_syntax.c_str(); + } + // Instance variables to hold the values for command options. + protected: + std::string m_help; + std::string m_syntax; + }; + + virtual Options * + GetOptions () + { + return &m_options; + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectCommandsAddRegex::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "help" , 'h', required_argument, NULL, 0, eArgTypeNone, "The help text to display for this command."}, +{ LLDB_OPT_SET_1, false, "syntax", 's', required_argument, NULL, 0, eArgTypeNone, "A syntax string showing the typical usage syntax."}, +{ 0 , false, NULL , 0 , 0 , NULL, 0, eArgTypeNone, NULL } +}; + + +class CommandObjectPythonFunction : public CommandObjectRaw +{ +private: + std::string m_function_name; + ScriptedCommandSynchronicity m_synchro; + bool m_fetched_help_long; + +public: + + CommandObjectPythonFunction (CommandInterpreter &interpreter, + std::string name, + std::string funct, + ScriptedCommandSynchronicity synch) : + CommandObjectRaw (interpreter, + name.c_str(), + (std::string("Run Python function ") + funct).c_str(), + NULL), + m_function_name(funct), + m_synchro(synch), + m_fetched_help_long(false) + { + } + + virtual + ~CommandObjectPythonFunction () + { + } + + virtual bool + IsRemovable () const + { + return true; + } + + const std::string& + GetFunctionName () + { + return m_function_name; + } + + ScriptedCommandSynchronicity + GetSynchronicity () + { + return m_synchro; + } + + virtual const char * + GetHelpLong () + { + if (!m_fetched_help_long) + { + ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); + if (scripter) + { + std::string docstring; + m_fetched_help_long = scripter->GetDocumentationForItem(m_function_name.c_str(),docstring); + if (!docstring.empty()) + SetHelpLong(docstring); + } + } + return CommandObjectRaw::GetHelpLong(); + } + +protected: + virtual bool + DoExecute (const char *raw_command_line, CommandReturnObject &result) + { + ScriptInterpreter* scripter = m_interpreter.GetScriptInterpreter(); + + Error error; + + result.SetStatus(eReturnStatusInvalid); + + if (!scripter || scripter->RunScriptBasedCommand(m_function_name.c_str(), + raw_command_line, + m_synchro, + result, + error) == false) + { + 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() == NULL || result.GetOutputData()[0] == '\0') + result.SetStatus(eReturnStatusSuccessFinishNoResult); + else + result.SetStatus(eReturnStatusSuccessFinishResult); + } + } + + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptImport +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptImport : public CommandObjectParsed +{ +public: + CommandObjectCommandsScriptImport (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script import", + "Import a scripting module in LLDB.", + NULL), + m_options(interpreter) + { + 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); + } + + ~CommandObjectCommandsScriptImport () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + m_allow_reload = true; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_allow_reload; + }; + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) + { + result.AppendError ("only scripting language supported for module importing is currently Python"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'command script import' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string path = command.GetArgumentAtIndex(0); + Error 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 (m_interpreter.GetScriptInterpreter()->LoadScriptingModule(path.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; +}; + +OptionDefinition +CommandObjectCommandsScriptImport::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "allow-reload", 'r', no_argument, NULL, 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."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptAdd +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptAdd : public CommandObjectParsed +{ +public: + CommandObjectCommandsScriptAdd(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script add", + "Add a scripted function as an LLDB command.", + NULL), + m_options (interpreter) + { + 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 () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': + m_funct_name = std::string(option_arg); + break; + case 's': + m_synchronous = (ScriptedCommandSynchronicity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); + if (!error.Success()) + error.SetErrorStringWithFormat ("unrecognized value for synchronicity '%s'", option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_funct_name = ""; + m_synchronous = eScriptedCommandSynchronicitySynchronous; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_funct_name; + ScriptedCommandSynchronicity m_synchronous; + }; + +private: + class PythonAliasReader : public InputReaderEZ + { + private: + CommandInterpreter& m_interpreter; + std::string m_cmd_name; + ScriptedCommandSynchronicity m_synchronous; + StringList m_user_input; + DISALLOW_COPY_AND_ASSIGN (PythonAliasReader); + public: + PythonAliasReader(Debugger& debugger, + CommandInterpreter& interpreter, + std::string cmd_name, + ScriptedCommandSynchronicity synch) : + InputReaderEZ(debugger), + m_interpreter(interpreter), + m_cmd_name(cmd_name), + m_synchronous(synch), + m_user_input() + {} + + virtual + ~PythonAliasReader() + { + } + + virtual void ActivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_python_command_instructions); + if (data.reader.GetPrompt()) + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + + virtual void ReactivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void GotTokenHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.bytes && data.bytes_len) + { + m_user_input.AppendString(data.bytes, data.bytes_len); + } + if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void InterruptHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + data.reader.SetIsDone (true); + if (!batch_mode) + { + out_stream->Printf ("Warning: No script attached.\n"); + out_stream->Flush(); + } + } + virtual void EOFHandler(HandlerData& data) + { + data.reader.SetIsDone (true); + } + virtual void DoneHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + + ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (!interpreter) + { + out_stream->Printf ("Script interpreter missing: no script attached.\n"); + out_stream->Flush(); + return; + } + std::string funct_name_str; + if (!interpreter->GenerateScriptAliasFunction (m_user_input, + funct_name_str)) + { + out_stream->Printf ("Unable to create function: no script attached.\n"); + out_stream->Flush(); + return; + } + if (funct_name_str.empty()) + { + out_stream->Printf ("Unable to obtain a function name: no script attached.\n"); + out_stream->Flush(); + return; + } + // everything should be fine now, let's add this alias + + CommandObjectSP command_obj_sp(new CommandObjectPythonFunction(m_interpreter, + m_cmd_name, + funct_name_str.c_str(), + m_synchronous)); + + if (!m_interpreter.AddUserCommand(m_cmd_name, command_obj_sp, true)) + { + out_stream->Printf ("Unable to add selected command: no script attached.\n"); + out_stream->Flush(); + return; + } + } + }; + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_interpreter.GetDebugger().GetScriptLanguage() != lldb::eScriptLanguagePython) + { + result.AppendError ("only scripting language supported for scripted commands is currently Python"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'command script add' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::string cmd_name = command.GetArgumentAtIndex(0); + + if (m_options.m_funct_name.empty()) + { + InputReaderSP reader_sp (new PythonAliasReader (m_interpreter.GetDebugger(), + m_interpreter, + cmd_name, + m_options.m_synchronous)); + + if (reader_sp) + { + + InputReaderEZ::InitializationParameters ipr; + + Error err (reader_sp->Initialize (ipr.SetBaton(NULL).SetPrompt(" "))); + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + CommandObjectSP new_cmd(new CommandObjectPythonFunction(m_interpreter, + cmd_name, + m_options.m_funct_name, + m_options.m_synchronous)); + if (m_interpreter.AddUserCommand(cmd_name, new_cmd, true)) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError("cannot add command"); + result.SetStatus (eReturnStatusFailed); + } + } + + return result.Succeeded(); + + } + + CommandOptions m_options; +}; + +static OptionEnumValueElement g_script_synchro_type[] = +{ + { eScriptedCommandSynchronicitySynchronous, "synchronous", "Run synchronous"}, + { eScriptedCommandSynchronicityAsynchronous, "asynchronous", "Run asynchronous"}, + { eScriptedCommandSynchronicityCurrentValue, "current", "Do not alter current setting"}, + { 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectCommandsScriptAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "function", 'f', required_argument, NULL, 0, eArgTypePythonFunction, "Name of the Python function to bind to this command name."}, + { LLDB_OPT_SET_1, false, "synchronicity", 's', required_argument, g_script_synchro_type, 0, eArgTypeScriptedCommandSynchronicity, "Set the synchronicity of this command's executions with regard to LLDB event system."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptList +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptList : public CommandObjectParsed +{ +private: + +public: + CommandObjectCommandsScriptList(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script list", + "List defined scripted commands.", + NULL) + { + } + + ~CommandObjectCommandsScriptList () + { + } + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + m_interpreter.GetHelp(result, + CommandInterpreter::eCommandTypesUserDef); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + return true; + + + } +}; + +//------------------------------------------------------------------------- +// CommandObjectCommandsScriptClear +//------------------------------------------------------------------------- + +class CommandObjectCommandsScriptClear : public CommandObjectParsed +{ +private: + +public: + CommandObjectCommandsScriptClear(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "command script clear", + "Delete all scripted commands.", + NULL) + { + } + + ~CommandObjectCommandsScriptClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + 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.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'command script delete' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char* cmd_name = command.GetArgumentAtIndex(0); + + if (cmd_name && *cmd_name && m_interpreter.HasUserCommands() && m_interpreter.UserCommandExists(cmd_name)) + { + m_interpreter.RemoveUser(cmd_name); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("command %s not found", cmd_name); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + + } +}; + +#pragma mark CommandObjectMultiwordCommandsScript + +//------------------------------------------------------------------------- +// CommandObjectMultiwordCommandsScript +//------------------------------------------------------------------------- + +class CommandObjectMultiwordCommandsScript : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordCommandsScript (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "command script", + "A set of commands for managing or customizing script commands.", + "command script []") + { + 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 () + { + } + +}; + + +#pragma mark CommandObjectMultiwordCommands + +//------------------------------------------------------------------------- +// CommandObjectMultiwordCommands +//------------------------------------------------------------------------- + +CommandObjectMultiwordCommands::CommandObjectMultiwordCommands (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "command", + "A set of commands for managing or customizing the debugger commands.", + "command []") +{ + LoadSubCommand ("source", CommandObjectSP (new CommandObjectCommandsSource (interpreter))); + LoadSubCommand ("alias", CommandObjectSP (new CommandObjectCommandsAlias (interpreter))); + LoadSubCommand ("unalias", CommandObjectSP (new CommandObjectCommandsUnalias (interpreter))); + LoadSubCommand ("regex", CommandObjectSP (new CommandObjectCommandsAddRegex (interpreter))); + LoadSubCommand ("history", CommandObjectSP (new CommandObjectCommandsHistory (interpreter))); + LoadSubCommand ("script", CommandObjectSP (new CommandObjectMultiwordCommandsScript (interpreter))); +} + +CommandObjectMultiwordCommands::~CommandObjectMultiwordCommands () +{ +} + diff --git a/source/Commands/CommandObjectCommands.h b/source/Commands/CommandObjectCommands.h new file mode 100644 index 00000000000..8a56e8dae6f --- /dev/null +++ b/source/Commands/CommandObjectCommands.h @@ -0,0 +1,40 @@ +//===-- CommandObjectCommands.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectCommands_h_ +#define liblldb_CommandObjectCommands_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordCommands +//------------------------------------------------------------------------- + +class CommandObjectMultiwordCommands : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordCommands (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordCommands (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectCommands_h_ diff --git a/source/Commands/CommandObjectDisassemble.cpp b/source/Commands/CommandObjectDisassemble.cpp new file mode 100644 index 00000000000..0d40fcd7c0b --- /dev/null +++ b/source/Commands/CommandObjectDisassemble.cpp @@ -0,0 +1,574 @@ +//===-- CommandObjectDisassemble.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectDisassemble.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.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; + +CommandObjectDisassemble::CommandOptions::CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + 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(); +} + +CommandObjectDisassemble::CommandOptions::~CommandOptions () +{ +} + +Error +CommandObjectDisassemble::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) +{ + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + bool success; + + switch (short_option) + { + case 'm': + show_mixed = true; + break; + + case 'C': + num_lines_context = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid num context lines string: \"%s\"", option_arg); + break; + + case 'c': + num_instructions = Args::StringToUInt32(option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid num of instructions string: \"%s\"", option_arg); + break; + + case 'b': + show_bytes = true; + break; + + case 's': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + start_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + if (start_addr != LLDB_INVALID_ADDRESS) + some_location_specified = true; + } + break; + case 'e': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + end_addr = Args::StringToAddress(&exe_ctx, 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': + { + Target *target = m_interpreter.GetExecutionContext().GetTargetPtr(); + if (target->GetArchitecture().GetTriple().getArch() == llvm::Triple::x86 + || target->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 (!arch.SetTriple (option_arg, m_interpreter.GetPlatform (true).get())) + arch.SetTriple (option_arg); + break; + + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + symbol_containing_addr = Args::StringToAddress(&exe_ctx, 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 () +{ + 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 = m_interpreter.GetExecutionContext().GetTargetPtr(); + + // 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; +} + +Error +CommandObjectDisassemble::CommandOptions::OptionParsingFinished () +{ + if (!some_location_specified) + current_function = true; + return Error(); + +} + +const OptionDefinition* +CommandObjectDisassemble::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +OptionDefinition +CommandObjectDisassemble::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "bytes" , 'b', no_argument , NULL, 0, eArgTypeNone, "Show opcode bytes when disassembling."}, +{ LLDB_OPT_SET_ALL, false, "context" , 'C', required_argument , NULL, 0, eArgTypeNumLines, "Number of context lines of source to show."}, +{ LLDB_OPT_SET_ALL, false, "mixed" , 'm', no_argument , NULL, 0, eArgTypeNone, "Enable mixed source and assembly display."}, +{ LLDB_OPT_SET_ALL, false, "raw" , 'r', no_argument , NULL, 0, eArgTypeNone, "Print raw disassembly with no symbol information."}, +{ LLDB_OPT_SET_ALL, false, "plugin" , 'P', required_argument , NULL, 0, eArgTypePlugin, "Name of the disassembler plugin you want to use."}, +{ LLDB_OPT_SET_ALL, false, "flavor" , 'F', required_argument , NULL, 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', required_argument , NULL, 0, eArgTypeArchitecture,"Specify the architecture to use from cross disassembly."}, +{ LLDB_OPT_SET_1 | + LLDB_OPT_SET_2 , true , "start-address", 's', required_argument , NULL, 0, eArgTypeAddressOrExpression,"Address at which to start disassembling."}, +{ LLDB_OPT_SET_1 , false, "end-address" , 'e', required_argument , NULL, 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', required_argument , NULL, 0, eArgTypeNumLines, "Number of instructions to display."}, +{ LLDB_OPT_SET_3 , false, "name" , 'n', required_argument , NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Disassemble entire contents of the given function name."}, +{ LLDB_OPT_SET_4 , false, "frame" , 'f', no_argument , NULL, 0, eArgTypeNone, "Disassemble from the start of the current frame's function."}, +{ LLDB_OPT_SET_5 , false, "pc" , 'p', no_argument , NULL, 0, eArgTypeNone, "Disassemble around the current pc."}, +{ LLDB_OPT_SET_6 , false, "line" , 'l', no_argument , NULL, 0, eArgTypeNone, "Disassemble the current frame's current source line instructions if there debug line table information, else disasemble around the pc."}, +{ LLDB_OPT_SET_7 , false, "address" , 'a', required_argument , NULL, 0, eArgTypeAddressOrExpression, "Disassemble function containing this address."}, +{ 0 , false, NULL , 0, 0 , NULL, 0, eArgTypeNone, NULL } +}; + + + +//------------------------------------------------------------------------- +// CommandObjectDisassemble +//------------------------------------------------------------------------- + +CommandObjectDisassemble::CommandObjectDisassemble (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "disassemble", + "Disassemble bytes in the current function, or elsewhere in the executable program as specified by the user.", + "disassemble []"), + m_options (interpreter) +{ +} + +CommandObjectDisassemble::~CommandObjectDisassemble() +{ +} + +bool +CommandObjectDisassemble::DoExecute (Args& command, CommandReturnObject &result) +{ + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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 architecure 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 != NULL && !disassembler->FlavorValidForArchSpec(m_options.arch, flavor_string)) + result.AppendWarningWithFormat("invalid disassembler flavor \"%s\", using default.\n", flavor_string); + + result.SetStatus (eReturnStatusSuccessFinishResult); + + if (command.GetArgumentCount() != 0) + { + result.AppendErrorWithFormat ("\"disassemble\" arguments are specified as options.\n"); + GetOptions()->GenerateOptionUsage (result.GetErrorStream(), this); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.show_mixed && m_options.num_lines_context == 0) + m_options.num_lines_context = 1; + + // 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 (m_interpreter.GetDebugger(), + m_options.arch, + plugin_name, + flavor_string, + m_exe_ctx, + name, + NULL, // Module * + m_options.num_instructions, + 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 + { + AddressRange range; + StackFrame *frame = m_exe_ctx.GetFramePtr(); + if (m_options.frame_line) + { + if (frame == NULL) + { + 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 == NULL) + { + 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 == NULL) + { + 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; + } + } + 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); + } + } + else + { + if (m_options.symbol_containing_addr != LLDB_INVALID_ADDRESS + && target + && !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; + module_sp->ResolveSymbolContextForAddress (symbol_containing_address, eSymbolContextEverything, sc); + 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; + } + } + } + } + } + + if (m_options.num_instructions != 0) + { + if (!range.GetBaseAddress().IsValid()) + { + // 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; + } + } + + if (Disassembler::Disassemble (m_interpreter.GetDebugger(), + m_options.arch, + plugin_name, + flavor_string, + m_exe_ctx, + range.GetBaseAddress(), + m_options.num_instructions, + 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); + } + } + else + { + if (!range.GetBaseAddress().IsValid()) + { + // 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; + } + } + if (range.GetByteSize() == 0) + range.SetByteSize(DEFAULT_DISASM_BYTE_SIZE); + + if (Disassembler::Disassemble (m_interpreter.GetDebugger(), + m_options.arch, + plugin_name, + flavor_string, + m_exe_ctx, + range, + m_options.num_instructions, + 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); + } + } + } + + return result.Succeeded(); +} diff --git a/source/Commands/CommandObjectDisassemble.h b/source/Commands/CommandObjectDisassemble.h new file mode 100644 index 00000000000..7a7509858b9 --- /dev/null +++ b/source/Commands/CommandObjectDisassemble.h @@ -0,0 +1,110 @@ +//===-- CommandObjectDisassemble.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectDisassemble_h_ +#define liblldb_CommandObjectDisassemble_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectDisassemble +//------------------------------------------------------------------------- + +class CommandObjectDisassemble : public CommandObjectParsed +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter); + + virtual + ~CommandOptions (); + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting (); + + const OptionDefinition* + GetDefinitions (); + + const char * + GetPluginName () + { + if (plugin_name.empty()) + return NULL; + return plugin_name.c_str(); + } + + const char * + GetFlavorString () + { + if (flavor_string.empty() || flavor_string == "default") + return NULL; + return flavor_string.c_str(); + } + + virtual Error + OptionParsingFinished (); + + 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; + static OptionDefinition g_option_table[]; + }; + + CommandObjectDisassemble (CommandInterpreter &interpreter); + + virtual + ~CommandObjectDisassemble (); + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + + CommandOptions m_options; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectDisassemble_h_ diff --git a/source/Commands/CommandObjectExpression.cpp b/source/Commands/CommandObjectExpression.cpp new file mode 100644 index 00000000000..da472d17331 --- /dev/null +++ b/source/Commands/CommandObjectExpression.cpp @@ -0,0 +1,505 @@ +//===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectExpression.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/DWARFExpression.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +CommandObjectExpression::CommandOptions::CommandOptions () : + OptionGroup() +{ +} + + +CommandObjectExpression::CommandOptions::~CommandOptions () +{ +} + +OptionDefinition +CommandObjectExpression::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads", 'a', required_argument, NULL, 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', required_argument, NULL, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Timeout value (in microseconds) for running the expression."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error", 'u', required_argument, NULL, 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)."}, +}; + + +uint32_t +CommandObjectExpression::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table)/sizeof(OptionDefinition); +} + +Error +CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + //case 'l': + //if (language.SetLanguageFromCString (option_arg) == false) + //{ + // error.SetErrorStringWithFormat("invalid language option argument '%s'", option_arg); + //} + //break; + + case 'a': + { + bool success; + bool result; + result = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid all-threads value setting: \"%s\"", option_arg); + else + try_all_threads = result; + } + break; + + case 'i': + { + bool success; + bool tmp_value = Args::StringToBoolean(option_arg, true, &success); + if (success) + ignore_breakpoints = tmp_value; + else + error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); + break; + } + case 't': + { + bool success; + uint32_t result; + result = Args::StringToUInt32(option_arg, 0, 0, &success); + if (success) + timeout = result; + else + error.SetErrorStringWithFormat ("invalid timeout setting \"%s\"", option_arg); + } + break; + + case 'u': + { + bool success; + bool tmp_value = Args::StringToBoolean(option_arg, true, &success); + if (success) + unwind_on_error = tmp_value; + else + error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); + break; + } + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + + return error; +} + +void +CommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter) +{ + Process *process = interpreter.GetExecutionContext().GetProcessPtr(); + if (process != NULL) + { + ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions(); + unwind_on_error = process->GetUnwindOnErrorInExpressions(); + } + else + { + ignore_breakpoints = false; + unwind_on_error = true; + } + + show_summary = true; + try_all_threads = true; + timeout = 0; +} + +const OptionDefinition* +CommandObjectExpression::CommandOptions::GetDefinitions () +{ + return g_option_table; +} + +CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "expression", + "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.", + NULL, + eFlagProcessMustBePaused | eFlagTryTargetAPILock), + m_option_group (interpreter), + m_format_options (eFormatDefault), + m_command_options (), + m_expr_line_count (0), + m_expr_lines () +{ + SetHelpLong( +"Timeouts:\n\ + If the expression can be evaluated statically (without runnning code) then it will be.\n\ + Otherwise, by default the expression will run on the current thread with a short timeout:\n\ + currently .25 seconds. If it doesn't return in that time, the evaluation will be interrupted\n\ + and resumed with all threads running. You can use the -a option to disable retrying on all\n\ + threads. You can use the -t option to set a shorter timeout.\n\ +\n\ +User defined variables:\n\ + You can define your own variables for convenience or to be used in subsequent expressions.\n\ + You define them the same way you would define variables in C. If the first character of \n\ + your user defined variable is a $, then the variable's value will be available in future\n\ + expressions, otherwise it will just be available in the current expression.\n\ +\n\ +Examples: \n\ +\n\ + expr my_struct->a = my_array[3] \n\ + expr -f bin -- (index * 8) + 5 \n\ + expr unsigned int $foo = 5\n\ + expr char c[] = \"foo\"; c[0]\n"); + + 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.Finalize(); +} + +CommandObjectExpression::~CommandObjectExpression () +{ +} + +Options * +CommandObjectExpression::GetOptions () +{ + return &m_option_group; +} + +size_t +CommandObjectExpression::MultiLineExpressionCallback +( + void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton; + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + StreamSP async_strm_sp(reader.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + async_strm_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); + async_strm_sp->Flush(); + } + } + // Fall through + case eInputReaderReactivate: + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + ++cmd_object_expr->m_expr_line_count; + if (bytes && bytes_len) + { + cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1); + } + + if (bytes_len == 0) + reader.SetIsDone(true); + break; + + case eInputReaderInterrupt: + cmd_object_expr->m_expr_lines.clear(); + reader.SetIsDone (true); + if (!batch_mode) + { + StreamSP async_strm_sp (reader.GetDebugger().GetAsyncOutputStream()); + if (async_strm_sp) + { + async_strm_sp->PutCString("Expression evaluation cancelled.\n"); + async_strm_sp->Flush(); + } + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + if (cmd_object_expr->m_expr_lines.size() > 0) + { + StreamSP output_stream = reader.GetDebugger().GetAsyncOutputStream(); + StreamSP error_stream = reader.GetDebugger().GetAsyncErrorStream(); + cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), + output_stream.get(), + error_stream.get()); + output_stream->Flush(); + error_stream->Flush(); + } + break; + } + + return bytes_len; +} + +bool +CommandObjectExpression::EvaluateExpression +( + const char *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 = Host::GetDummyTarget(m_interpreter.GetDebugger()).get(); + + if (target) + { + lldb::ValueObjectSP result_valobj_sp; + + ExecutionResults exe_results; + + bool keep_in_memory = true; + + EvaluateExpressionOptions options; + options.SetCoerceToId(m_varobj_options.use_objc) + .SetUnwindOnError(m_command_options.unwind_on_error) + .SetIgnoreBreakpoints (m_command_options.ignore_breakpoints) + .SetKeepInMemory(keep_in_memory) + .SetUseDynamic(m_varobj_options.use_dynamic) + .SetRunOthers(m_command_options.try_all_threads) + .SetTimeoutUsec(m_command_options.timeout); + + exe_results = target->EvaluateExpression (expr, + exe_ctx.GetFramePtr(), + result_valobj_sp, + options); + + 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); + + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(true,format)); + + ValueObject::DumpValueObject (*(output_stream), + result_valobj_sp.get(), // Variable object to dump + options); + if (result) + result->SetStatus (eReturnStatusSuccessFinishResult); + } + } + else + { + if (result_valobj_sp->GetError().GetError() == ClangUserExpression::kNoResult) + { + if (format != eFormatVoid && m_interpreter.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; +} + +bool +CommandObjectExpression::DoExecute +( + const char *command, + CommandReturnObject &result +) +{ + m_option_group.NotifyOptionParsingStarting(); + + const char * expr = NULL; + + if (command[0] == '\0') + { + m_expr_lines.clear(); + m_expr_line_count = 0; + + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (reader_sp) + { + Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + if (command[0] == '-') + { + // We have some options and these options MUST end with --. + const char *end_options = NULL; + const char *s = command; + while (s && s[0]) + { + end_options = ::strstr (s, "--"); + if (end_options) + { + end_options += 2; // Get past the "--" + if (::isspace (end_options[0])) + { + expr = end_options; + while (::isspace (*expr)) + ++expr; + break; + } + } + s = end_options; + } + + if (end_options) + { + Args args (command, end_options - command); + if (!ParseOptions (args, result)) + return false; + + Error error (m_option_group.NotifyOptionParsingFinished()); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (expr == NULL) + expr = command; + + if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result)) + return true; + + result.SetStatus (eReturnStatusFailed); + return false; +} + diff --git a/source/Commands/CommandObjectExpression.h b/source/Commands/CommandObjectExpression.h new file mode 100644 index 00000000000..3964f2d423e --- /dev/null +++ b/source/Commands/CommandObjectExpression.h @@ -0,0 +1,99 @@ +//===-- CommandObjectExpression.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectExpression_h_ +#define liblldb_CommandObjectExpression_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Target/ExecutionContext.h" + +namespace lldb_private { + +class CommandObjectExpression : public CommandObjectRaw +{ +public: + + class CommandOptions : public OptionGroup + { + public: + + CommandOptions (); + + virtual + ~CommandOptions (); + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions (); + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value); + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter); + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + bool unwind_on_error; + bool ignore_breakpoints; + bool show_types; + bool show_summary; + uint32_t timeout; + bool try_all_threads; + }; + + CommandObjectExpression (CommandInterpreter &interpreter); + + virtual + ~CommandObjectExpression (); + + virtual + Options * + GetOptions (); + +protected: + virtual bool + DoExecute (const char *command, + CommandReturnObject &result); + + static size_t + MultiLineExpressionCallback (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len); + + bool + EvaluateExpression (const char *expr, + Stream *output_stream, + Stream *error_stream, + CommandReturnObject *result = NULL); + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + OptionGroupValueObjectDisplay m_varobj_options; + CommandOptions m_command_options; + uint32_t m_expr_line_count; + std::string m_expr_lines; // Multi-line expression support +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectExpression_h_ diff --git a/source/Commands/CommandObjectFrame.cpp b/source/Commands/CommandObjectFrame.cpp new file mode 100644 index 00000000000..aace3a64960 --- /dev/null +++ b/source/Commands/CommandObjectFrame.cpp @@ -0,0 +1,624 @@ +//===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectFrame.h" + +// C Includes +// C++ Includes +#include +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionGroupVariable.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.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/Thread.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark CommandObjectFrameInfo + +//------------------------------------------------------------------------- +// CommandObjectFrameInfo +//------------------------------------------------------------------------- + +class CommandObjectFrameInfo : public CommandObjectParsed +{ +public: + + CommandObjectFrameInfo (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "frame info", + "List information about the currently selected frame in the current thread.", + "frame info", + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectFrameInfo () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat (&result.GetOutputStream()); + result.SetStatus (eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } +}; + +#pragma mark CommandObjectFrameSelect + +//------------------------------------------------------------------------- +// CommandObjectFrameSelect +//------------------------------------------------------------------------- + +class CommandObjectFrameSelect : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + bool success = false; + const int short_option = m_getopt_table[option_idx].val; + switch (short_option) + { + case 'r': + relative_frame_offset = Args::StringToSInt32 (option_arg, INT32_MIN, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid frame offset argument '%s'", option_arg); + break; + + default: + error.SetErrorStringWithFormat ("invalid short option character '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + relative_frame_offset = INT32_MIN; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + int32_t relative_frame_offset; + }; + + CommandObjectFrameSelect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "frame select", + "Select a frame by index from within the current thread and make it the current frame.", + NULL, + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + 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 () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "thread" for validity as eFlagRequiresThread 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 (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 (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) + { + const char *frame_idx_cstr = command.GetArgumentAtIndex(0); + frame_idx = Args::StringToUInt32 (frame_idx_cstr, UINT32_MAX, 0); + } + else if (command.GetArgumentCount() == 0) + { + frame_idx = thread->GetSelectedFrameIndex (); + if (frame_idx == UINT32_MAX) + { + frame_idx = 0; + } + } + else + { + result.AppendError ("invalid arguments.\n"); + m_options.GenerateOptionUsage (result.GetErrorStream(), this); + } + } + + 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; +}; + +OptionDefinition +CommandObjectFrameSelect::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "relative", 'r', required_argument, NULL, 0, eArgTypeOffset, "A relative frame index offset from the current frame index."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectFrameVariable +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectFrameVariable : public CommandObjectParsed +{ +public: + + CommandObjectFrameVariable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "frame variable", + "Show frame variables. All argument and local variables " + "that are in scope will be shown when no arguments are given. " + "If any arguments are specified, they can be names of " + "argument, local, file static and file global variables. " + "Children of aggregate variables can be specified such as " + "'var->child.x'.", + NULL, + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused | + eFlagRequiresProcess), + m_option_group (interpreter), + 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(); + } + + virtual + ~CommandObjectFrameVariable () + { + } + + virtual + Options * + GetOptions () + { + return &m_option_group; + } + + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + // Arguments are the standard source file completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eVariablePathCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "frame" for validity as eFlagRequiresFrame ensures it is valid + StackFrame *frame = m_exe_ctx.GetFramePtr(); + + Stream &s = result.GetOutputStream(); + + bool get_file_globals = true; + + // 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 (get_file_globals); + + VariableSP var_sp; + ValueObjectSP valobj_sp; + + const char *name_cstr = NULL; + size_t idx; + + 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.reset(new StringSummaryFormat(TypeSummaryImpl::Flags(),m_option_variable.summary_string.GetCurrentValue())); + + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(false,eFormatDefault,summary_format_sp)); + + if (variable_list) + { + const Format format = m_option_format.GetFormat(); + options.SetFormat(format); + + if (command.GetArgumentCount() > 0) + { + VariableList regex_var_list; + + // If we have any args to the variable command, we will make + // variable objects from them... + for (idx = 0; (name_cstr = command.GetArgumentAtIndex(idx)) != NULL; ++idx) + { + if (m_option_variable.use_regex) + { + const size_t regex_start_index = regex_var_list.GetSize(); + RegularExpression regex (name_cstr); + if (regex.Compile(name_cstr)) + { + 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) + { +// if (format != eFormatDefault) +// valobj_sp->SetFormat (format); + + 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 (": "); + } + ValueObject::DumpValueObject (result.GetOutputStream(), + valobj_sp.get(), + options); + } + } + } + } + else if (num_matches == 0) + { + result.GetErrorStream().Printf ("error: no variables matched the regular expression '%s'.\n", name_cstr); + } + } + 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: unkown regex error when compiling '%s'\n", name_cstr); + } + } + else // No regex, either exact variable names or variable expressions. + { + Error error; + uint32_t expr_path_options = StackFrame::eExpressionPathOptionCheckPtrVsMember | + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; + lldb::VariableSP var_sp; + valobj_sp = frame->GetValueForVariableExpressionPath (name_cstr, + m_varobj_options.use_dynamic, + expr_path_options, + var_sp, + error); + if (valobj_sp) + { +// if (format != eFormatDefault) +// valobj_sp->SetFormat (format); + if (m_option_variable.show_decl && var_sp && var_sp->GetDeclaration ().GetFile()) + { + var_sp->GetDeclaration ().DumpStopContext (&s, false); + s.PutCString (": "); + } + + options.SetFormat(format); + + Stream &output_stream = result.GetOutputStream(); + options.SetRootValueObjectName(valobj_sp->GetParent() ? name_cstr : NULL); + ValueObject::DumpValueObject (output_stream, + valobj_sp.get(), + options); + } + else + { + const char *error_cstr = error.AsCString(NULL); + 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", name_cstr); + } + } + } + } + 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; iGetVariableAtIndex(i); + bool dump_variable = true; + switch (var_sp->GetScope()) + { + case eValueTypeVariableGlobal: + dump_variable = m_option_variable.show_globals; + if (dump_variable && m_option_variable.show_scope) + s.PutCString("GLOBAL: "); + break; + + case eValueTypeVariableStatic: + dump_variable = m_option_variable.show_globals; + if (dump_variable && m_option_variable.show_scope) + s.PutCString("STATIC: "); + break; + + case eValueTypeVariableArgument: + dump_variable = m_option_variable.show_args; + if (dump_variable && m_option_variable.show_scope) + s.PutCString(" ARG: "); + break; + + case eValueTypeVariableLocal: + dump_variable = m_option_variable.show_locals; + if (dump_variable && m_option_variable.show_scope) + s.PutCString(" LOCAL: "); + break; + + default: + break; + } + + if (dump_variable) + { + // Use the variable object code to make sure we are + // using the same APIs as the the public API will be + // using... + valobj_sp = frame->GetValueObjectForFrameVariable (var_sp, + m_varobj_options.use_dynamic); + if (valobj_sp) + { +// if (format != eFormatDefault) +// valobj_sp->SetFormat (format); + + // When dumping all variables, don't print any variables + // that are not in scope to avoid extra unneeded output + if (valobj_sp->IsInScope ()) + { + if (m_option_variable.show_decl && var_sp->GetDeclaration ().GetFile()) + { + var_sp->GetDeclaration ().DumpStopContext (&s, false); + s.PutCString (": "); + } + + options.SetFormat(format); + options.SetRootValueObjectName(name_cstr); + ValueObject::DumpValueObject (result.GetOutputStream(), + valobj_sp.get(), + options); + } + } + } + } + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + + if (m_interpreter.TruncationWarningNecessary()) + { + result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(), + m_cmd_name.c_str()); + m_interpreter.TruncationWarningGiven(); + } + + return result.Succeeded(); + } +protected: + + OptionGroupOptions m_option_group; + OptionGroupVariable m_option_variable; + OptionGroupFormat m_option_format; + OptionGroupValueObjectDisplay m_varobj_options; +}; + + +#pragma mark CommandObjectMultiwordFrame + +//------------------------------------------------------------------------- +// CommandObjectMultiwordFrame +//------------------------------------------------------------------------- + +CommandObjectMultiwordFrame::CommandObjectMultiwordFrame (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "frame", + "A set of commands for operating on the current thread's frames.", + "frame []") +{ + LoadSubCommand ("info", CommandObjectSP (new CommandObjectFrameInfo (interpreter))); + LoadSubCommand ("select", CommandObjectSP (new CommandObjectFrameSelect (interpreter))); + LoadSubCommand ("variable", CommandObjectSP (new CommandObjectFrameVariable (interpreter))); +} + +CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame () +{ +} + diff --git a/source/Commands/CommandObjectFrame.h b/source/Commands/CommandObjectFrame.h new file mode 100644 index 00000000000..ea7c808e84b --- /dev/null +++ b/source/Commands/CommandObjectFrame.h @@ -0,0 +1,40 @@ +//===-- CommandObjectFrame.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectFrame_h_ +#define liblldb_CommandObjectFrame_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordFrame +//------------------------------------------------------------------------- + +class CommandObjectMultiwordFrame : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordFrame (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordFrame (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectFrame_h_ diff --git a/source/Commands/CommandObjectHelp.cpp b/source/Commands/CommandObjectHelp.cpp new file mode 100644 index 00000000000..d2c97f91260 --- /dev/null +++ b/source/Commands/CommandObjectHelp.cpp @@ -0,0 +1,249 @@ +//===-- CommandObjectHelp.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectHelp.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectHelp +//------------------------------------------------------------------------- + +CommandObjectHelp::CommandObjectHelp (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "help", + "Show a list of all debugger commands, or give details about specific commands.", + "help []"), m_options (interpreter) +{ + 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() +{ +} + +OptionDefinition +CommandObjectHelp::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "show-aliases", 'a', no_argument, NULL, 0, eArgTypeNone, "Show aliases in the command list."}, + { LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', no_argument, NULL, 0, eArgTypeNone, "Hide user-defined commands from the list."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +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; + + 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; + cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex (0), &matches); + bool is_alias_command = m_interpreter.AliasExists (command.GetArgumentAtIndex (0)); + std::string alias_name = command.GetArgumentAtIndex(0); + + if (cmd_obj != NULL) + { + 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. + for (size_t i = 1; i < argc && all_okay; ++i) + { + std::string sub_command = command.GetArgumentAtIndex(i); + matches.Clear(); + if (! sub_cmd_obj->IsMultiwordObject ()) + { + all_okay = false; + } + else + { + CommandObject *found_cmd; + found_cmd = sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches); + if (found_cmd == NULL) + all_okay = false; + else if (matches.GetSize() > 1) + all_okay = false; + else + sub_cmd_obj = found_cmd; + } + } + + if (!all_okay || (sub_cmd_obj == NULL)) + { + 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.GetData()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else if (!sub_cmd_obj) + { + result.AppendErrorWithFormat("'%s' is not a known command.\n" + "Try 'help' to see a current list of commands.\n", + cmd_string.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + result.GetOutputStream().Printf("'%s' is not a known command.\n" + "Try 'help' to see a current list of commands.\n" + "The closest match is '%s'. Help on it follows.\n\n", + cmd_string.c_str(), + sub_cmd_obj->GetCommandName()); + } + } + + sub_cmd_obj->GenerateHelpText(result); + + if (is_alias_command) + { + StreamString sstr; + m_interpreter.GetAliasHelp (alias_name.c_str(), cmd_obj->GetCommandName(), sstr); + result.GetOutputStream().Printf ("\n'%s' is an abbreviation for %s\n", alias_name.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.GetArgumentAtIndex (0)); + if (arg_type != eArgTypeLastArg) + { + Stream &output_strm = result.GetOutputStream (); + CommandObject::GetArgumentHelp (output_strm, arg_type, m_interpreter); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat + ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n", + command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} + +int +CommandObjectHelp::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches +) +{ + // Return the completions of the commands in the help system: + if (cursor_index == 0) + { + return m_interpreter.HandleCompletionMatches (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + else + { + CommandObject *cmd_obj = m_interpreter.GetCommandObject (input.GetArgumentAtIndex(0)); + + // 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) + { + input.Shift(); + cursor_index--; + return cmd_obj->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + else + { + return m_interpreter.HandleCompletionMatches (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + } +} diff --git a/source/Commands/CommandObjectHelp.h b/source/Commands/CommandObjectHelp.h new file mode 100644 index 00000000000..6e8f9d4cbc7 --- /dev/null +++ b/source/Commands/CommandObjectHelp.h @@ -0,0 +1,119 @@ +//===-- CommandObjectHelp.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectHelp_h_ +#define liblldb_CommandObjectHelp_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectHelp +//------------------------------------------------------------------------- + +class CommandObjectHelp : public CommandObjectParsed +{ +public: + + CommandObjectHelp (CommandInterpreter &interpreter); + + virtual + ~CommandObjectHelp (); + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches); + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + m_show_aliases = true; + break; + case 'u': + m_show_user_defined = false; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_show_aliases = false; + m_show_user_defined = true; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_show_aliases; + bool m_show_user_defined; + }; + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + +private: + CommandOptions m_options; + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectHelp_h_ diff --git a/source/Commands/CommandObjectLog.cpp b/source/Commands/CommandObjectLog.cpp new file mode 100644 index 00000000000..5fb79154c4e --- /dev/null +++ b/source/Commands/CommandObjectLog.cpp @@ -0,0 +1,503 @@ +//===-- CommandObjectLog.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectLog.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.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" + +using namespace lldb; +using namespace lldb_private; + + +class CommandObjectLogEnable : public CommandObjectParsed +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogEnable(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "log enable", + "Enable logging for a single log channel.", + NULL), + m_options (interpreter) + { + + 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); + } + + virtual + ~CommandObjectLogEnable() + { + } + + Options * + GetOptions () + { + return &m_options; + } + +// int +// HandleArgumentCompletion (Args &input, +// int &cursor_index, +// int &cursor_char_position, +// OptionElementVector &opt_element_vector, +// int match_start_point, +// int max_return_elements, +// bool &word_complete, +// StringList &matches) +// { +// std::string completion_str (input.GetArgumentAtIndex(cursor_index)); +// completion_str.erase (cursor_char_position); +// +// if (cursor_index == 1) +// { +// // +// Log::AutoCompleteChannelName (completion_str.c_str(), matches); +// } +// return matches.GetSize(); +// } +// + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + log_file (), + log_options (0) + { + } + + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'f': log_file.SetFile(option_arg, true); break; + case 't': log_options |= LLDB_LOG_OPTION_THREADSAFE; break; + case 'v': log_options |= LLDB_LOG_OPTION_VERBOSE; break; + case 'g': log_options |= LLDB_LOG_OPTION_DEBUG; 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; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + log_file.Clear(); + log_options = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + FileSpec log_file; + uint32_t log_options; + }; + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + if (args.GetArgumentCount() < 2) + { + result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str()); + } + else + { + std::string channel(args.GetArgumentAtIndex(0)); + 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'; + bool success = m_interpreter.GetDebugger().EnableLog (channel.c_str(), + args.GetConstArgumentVector(), + log_file, + m_options.log_options, + result.GetErrorStream()); + if (success) + result.SetStatus (eReturnStatusSuccessFinishNoResult); + else + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectLogEnable::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, 0, eArgTypeFilename, "Set the destination file to log to."}, +{ LLDB_OPT_SET_1, false, "threadsafe", 't', no_argument, NULL, 0, eArgTypeNone, "Enable thread safe logging to avoid interweaved log lines." }, +{ LLDB_OPT_SET_1, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, "Enable verbose logging." }, +{ LLDB_OPT_SET_1, false, "debug", 'g', no_argument, NULL, 0, eArgTypeNone, "Enable debug logging." }, +{ LLDB_OPT_SET_1, false, "sequence", 's', no_argument, NULL, 0, eArgTypeNone, "Prepend all log lines with an increasing integer sequence id." }, +{ LLDB_OPT_SET_1, false, "timestamp", 'T', no_argument, NULL, 0, eArgTypeNone, "Prepend all log lines with a timestamp." }, +{ LLDB_OPT_SET_1, false, "pid-tid", 'p', no_argument, NULL, 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', no_argument, NULL, 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', no_argument, NULL, 0, eArgTypeNone, "Append a stack backtrace to each log line." }, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectLogDisable : public CommandObjectParsed +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLogDisable(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "log disable", + "Disable one or more log channel categories.", + NULL) + { + 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); + } + + virtual + ~CommandObjectLogDisable() + { + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + result.AppendErrorWithFormat("%s takes a log channel and one or more log types.\n", m_cmd_name.c_str()); + } + else + { + Log::Callbacks log_callbacks; + + std::string channel(args.GetArgumentAtIndex(0)); + args.Shift (); // Shift off the channel + if (Log::GetLogChannelCallbacks (ConstString(channel.c_str()), log_callbacks)) + { + log_callbacks.disable (args.GetConstArgumentVector(), &result.GetErrorStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else if (channel == "all") + { + Log::DisableAllLogChannels(&result.GetErrorStream()); + } + else + { + LogChannelSP log_channel_sp (LogChannel::FindPlugin(channel.c_str())); + if (log_channel_sp) + { + log_channel_sp->Disable(args.GetConstArgumentVector(), &result.GetErrorStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); + } + } + 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.", + NULL) + { + 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); + } + + virtual + ~CommandObjectLogList() + { + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + if (argc == 0) + { + Log::ListAllLogChannels (&result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else + { + for (size_t i=0; iListCategories(&result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendErrorWithFormat("Invalid log channel '%s'.\n", args.GetArgumentAtIndex(0)); + } + } + } + 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 | disable | dump | increment | reset >") + { + } + + virtual + ~CommandObjectLogTimer() + { + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + result.SetStatus(eReturnStatusFailed); + + if (argc == 1) + { + const char *sub_command = args.GetArgumentAtIndex(0); + + if (strcasecmp(sub_command, "enable") == 0) + { + Timer::SetDisplayDepth (UINT32_MAX); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else if (strcasecmp(sub_command, "disable") == 0) + { + Timer::DumpCategoryTimes (&result.GetOutputStream()); + Timer::SetDisplayDepth (0); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else if (strcasecmp(sub_command, "dump") == 0) + { + Timer::DumpCategoryTimes (&result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + else if (strcasecmp(sub_command, "reset") == 0) + { + Timer::ResetCategoryTimes (); + result.SetStatus(eReturnStatusSuccessFinishResult); + } + + } + else if (argc == 2) + { + const char *sub_command = args.GetArgumentAtIndex(0); + + if (strcasecmp(sub_command, "enable") == 0) + { + bool success; + uint32_t depth = Args::StringToUInt32(args.GetArgumentAtIndex(1), 0, 0, &success); + if (success) + { + Timer::SetDisplayDepth (depth); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + result.AppendError("Could not convert enable depth to an unsigned integer."); + } + if (strcasecmp(sub_command, "increment") == 0) + { + bool success; + bool increment = Args::StringToBoolean(args.GetArgumentAtIndex(1), 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 constructor +//---------------------------------------------------------------------- +CommandObjectLog::CommandObjectLog(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "log", + "A set of commands for operating on logs.", + "log []") +{ + LoadSubCommand ("enable", CommandObjectSP (new CommandObjectLogEnable (interpreter))); + LoadSubCommand ("disable", CommandObjectSP (new CommandObjectLogDisable (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectLogList (interpreter))); + LoadSubCommand ("timers", CommandObjectSP (new CommandObjectLogTimer (interpreter))); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectLog::~CommandObjectLog() +{ +} + + + + diff --git a/source/Commands/CommandObjectLog.h b/source/Commands/CommandObjectLog.h new file mode 100644 index 00000000000..3e731fa1d18 --- /dev/null +++ b/source/Commands/CommandObjectLog.h @@ -0,0 +1,48 @@ +//===-- CommandObjectLog.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectLog_h_ +#define liblldb_CommandObjectLog_h_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectLog +//------------------------------------------------------------------------- + +class CommandObjectLog : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectLog(CommandInterpreter &interpreter); + + virtual + ~CommandObjectLog(); + +private: + //------------------------------------------------------------------ + // For CommandObjectLog only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectLog); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectLog_h_ diff --git a/source/Commands/CommandObjectMemory.cpp b/source/Commands/CommandObjectMemory.cpp new file mode 100644 index 00000000000..4725a4da657 --- /dev/null +++ b/source/Commands/CommandObjectMemory.cpp @@ -0,0 +1,1370 @@ +//===-- CommandObjectMemory.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupOutputFile.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Symbol/TypeList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" + +using namespace lldb; +using namespace lldb_private; + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "num-per-line" ,'l', required_argument, NULL, 0, eArgTypeNumberPerLine ,"The number of items per line to display."}, + { LLDB_OPT_SET_2, false, "binary" ,'b', no_argument , NULL, 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, true , "type" ,'t', required_argument, NULL, 0, eArgTypeNone ,"The name of a type to view memory as."}, + { LLDB_OPT_SET_1| + LLDB_OPT_SET_2| + LLDB_OPT_SET_3, false, "force" ,'r', no_argument, NULL, 0, eArgTypeNone ,"Necessary if reading over target.max-memory-read-size bytes."}, +}; + + + +class OptionGroupReadMemory : public OptionGroup +{ +public: + + OptionGroupReadMemory () : + m_num_per_line (1,1), + m_output_as_binary (false), + m_view_as_type() + { + } + + virtual + ~OptionGroupReadMemory () + { + } + + + virtual uint32_t + GetNumDefinitions () + { + return sizeof (g_option_table) / sizeof (OptionDefinition); + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'l': + error = m_num_per_line.SetValueFromCString (option_arg); + if (m_num_per_line.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid value for --num-per-line option '%s'", option_arg); + break; + + case 'b': + m_output_as_binary = true; + break; + + case 't': + error = m_view_as_type.SetValueFromCString (option_arg); + break; + + case 'r': + m_force = true; + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_num_per_line.Clear(); + m_output_as_binary = false; + m_view_as_type.Clear(); + m_force = false; + } + + Error + FinalizeSettings (Target *target, OptionGroupFormat& format_options) + { + Error 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 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(); + } + + OptionValueUInt64 m_num_per_line; + bool m_output_as_binary; + OptionValueString m_view_as_type; + bool m_force; +}; + + + +//---------------------------------------------------------------------- +// Read memory from the inferior process +//---------------------------------------------------------------------- +class CommandObjectMemoryRead : public CommandObjectParsed +{ +public: + + CommandObjectMemoryRead (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "memory read", + "Read from the memory of the process being debugged.", + NULL, + eFlagRequiresTarget | eFlagProcessMustBePaused), + m_option_group (interpreter), + 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(); + } + + virtual + ~CommandObjectMemoryRead () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + return m_cmd_name.c_str(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "target" for validity as eFlagRequiresTarget 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; + } + + ClangASTType clang_ast_type; + Error 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 + + SymbolContext sc; + 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; + // fall through... + 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; + } + } + } + + ConstString lookup_type_name(type_str.c_str()); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + if (frame) + { + sc = frame->GetSymbolContext (eSymbolContextModule); + if (sc.module_sp) + { + sc.module_sp->FindTypes (sc, + lookup_type_name, + exact_match, + 1, + type_list); + } + } + if (type_list.GetSize() == 0) + { + target->GetImages().FindTypes (sc, + lookup_type_name, + exact_match, + 1, + type_list); + } + + if (type_list.GetSize() == 0 && lookup_type_name.GetCString() && *lookup_type_name.GetCString() == '$') + { + clang::TypeDecl *tdecl = target->GetPersistentVariables().GetPersistentType(ConstString(lookup_type_name)); + if (tdecl) + { + clang_ast_type.SetClangType(&tdecl->getASTContext(),(lldb::clang_type_t)tdecl->getTypeForDecl()); + } + } + + if (clang_ast_type.IsValid() == false) + { + 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)); + clang_ast_type = type_sp->GetClangFullType(); + } + } + + while (pointer_count > 0) + { + ClangASTType pointer_type = clang_ast_type.GetPointerType(); + if (pointer_type.IsValid()) + clang_ast_type = pointer_type; + else + { + result.AppendError ("unable make a pointer type\n"); + result.SetStatus(eReturnStatusFailed); + return false; + } + --pointer_count; + } + + m_format_options.GetByteSizeValue() = clang_ast_type.GetByteSize(); + + if (m_format_options.GetByteSizeValue() == 0) + { + result.AppendErrorWithFormat ("unable to get the byte size of the type '%s'\n", + view_as_type_cstr); + result.SetStatus(eReturnStatusFailed); + return false; + } + + 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; + clang_ast_type = m_prev_clang_ast_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(); + size_t item_byte_size = 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 = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(0), 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 = Args::StringToAddress(&m_exe_ctx, command.GetArgumentAtIndex(1), LLDB_INVALID_ADDRESS, 0); + 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 %lu), not both.\n", end_addr, 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 (clang_ast_type.GetOpaqueQualType()) + { + // Make sure we don't display our type as ASCII bytes like the default memory read + if (m_format_options.GetFormatValue().OptionWasSet() == false) + m_format_options.GetFormatValue().SetCurrentValue(eFormatDefault); + + bytes_read = clang_ast_type.GetByteSize() * m_format_options.GetCountValue().GetCurrentValue(); + } + else if (m_format_options.GetFormatValue().GetCurrentValue() != eFormatCString) + { + data_sp.reset (new DataBufferHeap (total_byte_size, '\0')); + if (data_sp->GetBytes() == NULL) + { + result.AppendErrorWithFormat ("can't allocate 0x%zx bytes for the memory read buffer, specify a smaller size to read", total_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Address address(addr, NULL); + 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 (%lu/%lu) were able to be read from 0x%" PRIx64 ".\n", bytes_read, 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.reset (new DataBufferHeap ((item_byte_size+1) * item_count, '\0')); // account for NULLs as necessary + if (data_sp->GetBytes() == NULL) + { + 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; + while (item_count < count) + { + std::string buffer; + buffer.resize(item_byte_size+1,0); + Error 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); + break; + } + read+=1; // 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 + } + data_sp.reset(new 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_clang_ast_type = clang_ast_type; + + StreamFile outfile_stream; + Stream *output_stream = NULL; + const FileSpec &outfile_spec = m_outfile_options.GetFile().GetCurrentValue(); + if (outfile_spec) + { + char path[PATH_MAX]; + outfile_spec.GetPath (path, sizeof(path)); + + uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate; + const bool append = m_outfile_options.GetAppend().GetCurrentValue(); + if (append) + open_options |= File::eOpenOptionAppend; + + if (outfile_stream.GetFile ().Open (path, open_options).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); + return true; + } + else + { + result.AppendErrorWithFormat("Failed to write %" PRIu64 " bytes to '%s'.\n", (uint64_t)bytes_read, path); + 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, append ? "append" : "write"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + output_stream = &result.GetOutputStream(); + } + + + ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); + if (clang_ast_type.GetOpaqueQualType()) + { + for (uint32_t i = 0; iSetFormat (format); + + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(false,format)); + + ValueObject::DumpValueObject (*output_stream, + valobj_sp.get(), + options); + } + else + { + result.AppendErrorWithFormat ("failed to create a value object for: (%s) %s\n", + view_as_type_cstr, + name_strm.GetString().c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + return true; + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + DataExtractor data (data_sp, + target->GetArchitecture().GetByteOrder(), + target->GetArchitecture().GetAddressByteSize()); + + Format format = m_format_options.GetFormat(); + if ( ( (format == eFormatChar) || (format == eFormatCharPrintable) ) + && (item_byte_size != 1) + && (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; + } + + assert (output_stream); + size_t bytes_dumped = data.Dump (output_stream, + 0, + format, + item_byte_size, + item_count, + num_per_line, + 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; + ClangASTType m_prev_clang_ast_type; +}; + + +OptionDefinition +g_memory_write_option_table[] = +{ +{ LLDB_OPT_SET_1, true, "infile", 'i', required_argument, NULL, 0, eArgTypeFilename, "Write memory using the contents of a file."}, +{ LLDB_OPT_SET_1, false, "offset", 'o', required_argument, NULL, 0, eArgTypeOffset, "Start writng bytes from an offset within the input file."}, +}; + + +//---------------------------------------------------------------------- +// Write memory to the inferior process +//---------------------------------------------------------------------- +class CommandObjectMemoryWrite : public CommandObjectParsed +{ +public: + + class OptionGroupWriteMemory : public OptionGroup + { + public: + OptionGroupWriteMemory () : + OptionGroup() + { + } + + virtual + ~OptionGroupWriteMemory () + { + } + + virtual uint32_t + GetNumDefinitions () + { + return sizeof (g_memory_write_option_table) / sizeof (OptionDefinition); + } + + virtual const OptionDefinition* + GetDefinitions () + { + return g_memory_write_option_table; + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) + { + Error error; + const int short_option = g_memory_write_option_table[option_idx].short_option; + + switch (short_option) + { + case 'i': + m_infile.SetFile (option_arg, true); + if (!m_infile.Exists()) + { + m_infile.Clear(); + error.SetErrorStringWithFormat("input file does not exist: '%s'", option_arg); + } + break; + + case 'o': + { + bool success; + m_infile_offset = Args::StringToUInt64(option_arg, 0, 0, &success); + if (!success) + { + error.SetErrorStringWithFormat("invalid offset string '%s'", option_arg); + } + } + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + return error; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + 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 process being debugged.", + NULL, + eFlagRequiresProcess | eFlagProcessMustBeLaunched), + m_option_group (interpreter), + 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(); + + } + + virtual + ~CommandObjectMemoryWrite () + { + } + + Options * + GetOptions () + { + 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: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + // No need to check "process" for validity as eFlagRequiresProcess 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(); + + Error error; + lldb::addr_t addr = Args::StringToAddress (&m_exe_ctx, + command.GetArgumentAtIndex(0), + 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 > 0) + length = item_byte_size; + lldb::DataBufferSP data_sp (m_memory_options.m_infile.ReadFileContents (m_memory_options.m_infile_offset, length)); + if (data_sp) + { + length = data_sp->GetByteSize(); + if (length > 0) + { + Error 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; + const size_t num_value_args = command.GetArgumentCount(); + for (size_t i=0; iWriteMemory (addr, value_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: + sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("'%s' is not a valid signed decimal value.\n", value_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 %lu byte signed integer value.\n", sval64, item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64 (sval64, item_byte_size); + break; + + case eFormatUnsigned: + uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("'%s' is not a valid unsigned decimal string value.\n", value_str); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (!UIntValueIsValidForSize (uval64, item_byte_size)) + { + result.AppendErrorWithFormat ("Value %" PRIu64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64 (uval64, item_byte_size); + break; + + case eFormatOctal: + uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 8, &success); + if (!success) + { + result.AppendErrorWithFormat ("'%s' is not a valid octal string value.\n", value_str); + result.SetStatus(eReturnStatusFailed); + return false; + } + else if (!UIntValueIsValidForSize (uval64, item_byte_size)) + { + result.AppendErrorWithFormat ("Value %" PRIo64 " is too large to fit in a %lu byte unsigned integer value.\n", uval64, item_byte_size); + result.SetStatus(eReturnStatusFailed); + return false; + } + buffer.PutMaxHex64 (uval64, item_byte_size); + break; + } + } + + if (!buffer.GetString().empty()) + { + Error error; + if (process->WriteMemory (addr, buffer.GetString().c_str(), 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; +}; + + +//------------------------------------------------------------------------- +// CommandObjectMemory +//------------------------------------------------------------------------- + +CommandObjectMemory::CommandObjectMemory (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "memory", + "A set of commands for operating on memory.", + "memory []") +{ + LoadSubCommand ("read", CommandObjectSP (new CommandObjectMemoryRead (interpreter))); + LoadSubCommand ("write", CommandObjectSP (new CommandObjectMemoryWrite (interpreter))); +} + +CommandObjectMemory::~CommandObjectMemory () +{ +} diff --git a/source/Commands/CommandObjectMemory.h b/source/Commands/CommandObjectMemory.h new file mode 100644 index 00000000000..b044921ae07 --- /dev/null +++ b/source/Commands/CommandObjectMemory.h @@ -0,0 +1,33 @@ +//===-- CommandObjectMemory.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectMemory_h_ +#define liblldb_CommandObjectMemory_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +class CommandObjectMemory : public CommandObjectMultiword +{ +public: + CommandObjectMemory (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMemory (); +}; + + +} // namespace lldb_private + +#endif // liblldb_CommandObjectMemory_h_ diff --git a/source/Commands/CommandObjectMultiword.cpp b/source/Commands/CommandObjectMultiword.cpp new file mode 100644 index 00000000000..f84b401f3aa --- /dev/null +++ b/source/Commands/CommandObjectMultiword.cpp @@ -0,0 +1,520 @@ +//===-- CommandObjectMultiword.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/CommandObjectMultiword.h" +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.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 () +{ +} + +CommandObjectSP +CommandObjectMultiword::GetSubcommandSP (const char *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 == NULL) + matches = &local_matches; + int num_matches = CommandObject::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 (const char *sub_cmd, StringList *matches) +{ + return GetSubcommandSP(sub_cmd, matches).get(); +} + +bool +CommandObjectMultiword::LoadSubCommand +( + const char *name, + const CommandObjectSP& cmd_obj +) +{ + 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); + } + else + { + const char *sub_command = args.GetArgumentAtIndex (0); + + if (sub_command) + { + if (::strcasecmp (sub_command, "help") == 0) + { + this->CommandObject::GenerateHelpText (result); + } + else if (!m_subcommand_dict.empty()) + { + StringList matches; + CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches); + if (sub_cmd_obj != NULL) + { + // Now call CommandObject::Execute to process and 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); + } + else + { + 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 < num_subcmd_matches; 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); + } + } + else + { + result.AppendErrorWithFormat ("'%s' does not have any subcommands.\n", GetCommandName()); + result.SetStatus (eReturnStatusFailed); + } + } + } + + return result.Succeeded(); +} + +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 + + output_stream.PutCString ("The following subcommands are supported:\n\n"); + + CommandMap::iterator pos; + uint32_t max_len = m_interpreter.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 (" This command takes 'raw' input (no need to quote stuff)."); + 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 '.\n"); +} + +int +CommandObjectMultiword::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches +) +{ + // Any of the command matches will provide a complete word, otherwise the individual + // completers will override this. + word_complete = true; + + if (cursor_index == 0) + { + CommandObject::AddNamesMatchingPartialString (m_subcommand_dict, + input.GetArgumentAtIndex(0), + matches); + + if (matches.GetSize() == 1 + && matches.GetStringAtIndex(0) != NULL + && strcmp (input.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + { + StringList temp_matches; + CommandObject *cmd_obj = GetSubcommandObject (input.GetArgumentAtIndex(0), + &temp_matches); + if (cmd_obj != NULL) + { + matches.DeleteStringAtIndex (0); + input.Shift(); + cursor_char_position = 0; + input.AppendArgument (""); + return cmd_obj->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + else + return matches.GetSize(); + } + else + return matches.GetSize(); + } + else + { + CommandObject *sub_command_object = GetSubcommandObject (input.GetArgumentAtIndex(0), + &matches); + if (sub_command_object == NULL) + { + return matches.GetSize(); + } + else + { + // Remove the one match that we got from calling GetSubcommandObject. + matches.DeleteStringAtIndex(0); + input.Shift(); + cursor_index--; + return sub_command_object->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + + } +} + +const char * +CommandObjectMultiword::GetRepeatCommand (Args ¤t_command_args, uint32_t index) +{ + index++; + if (current_command_args.GetArgumentCount() <= index) + return NULL; + CommandObject *sub_command_object = GetSubcommandObject (current_command_args.GetArgumentAtIndex(index)); + if (sub_command_object == NULL) + return NULL; + return sub_command_object->GetRepeatCommand(current_command_args, index); +} + + +void +CommandObjectMultiword::AproposAllSubCommands (const char *prefix, + const char *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.Printf ("%s %s", prefix, command_name); + + if (sub_cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (complete_command_name.GetData()); + commands_help.AppendString (sub_cmd_obj->GetHelp()); + } + + if (sub_cmd_obj->IsMultiwordObject()) + sub_cmd_obj->AproposAllSubCommands (complete_command_name.GetData(), + 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 () +{ +} + +const char * +CommandObjectProxy::GetHelpLong () +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetHelpLong(); + return NULL; +} + +bool +CommandObjectProxy::IsRemovable() const +{ + const CommandObject *proxy_command = const_cast(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; +} + +lldb::CommandObjectSP +CommandObjectProxy::GetSubcommandSP (const char *sub_cmd, StringList *matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetSubcommandSP(sub_cmd, matches); + return lldb::CommandObjectSP(); +} + +CommandObject * +CommandObjectProxy::GetSubcommandObject (const char *sub_cmd, StringList *matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetSubcommandObject(sub_cmd, matches); + return NULL; +} + +void +CommandObjectProxy::AproposAllSubCommands (const char *prefix, + const char *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 (const char *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 NULL; +} + + +int +CommandObjectProxy::HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->HandleCompletion (input, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + matches.Clear(); + return 0; +} +int +CommandObjectProxy::HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->HandleArgumentCompletion (input, + cursor_index, + cursor_char_position, + opt_element_vector, + match_start_point, + max_return_elements, + word_complete, + matches); + matches.Clear(); + return 0; +} + +const char * +CommandObjectProxy::GetRepeatCommand (Args ¤t_command_args, + uint32_t index) +{ + CommandObject *proxy_command = GetProxyCommandObject(); + if (proxy_command) + return proxy_command->GetRepeatCommand (current_command_args, index); + return NULL; +} + +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/source/Commands/CommandObjectPlatform.cpp b/source/Commands/CommandObjectPlatform.cpp new file mode 100644 index 00000000000..c2185e598ad --- /dev/null +++ b/source/Commands/CommandObjectPlatform.cpp @@ -0,0 +1,987 @@ +//===-- CommandObjectPlatform.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectPlatform.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + + +//---------------------------------------------------------------------- +// "platform select " +//---------------------------------------------------------------------- +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 ", + 0), + m_option_group (interpreter), + 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(); + } + + virtual + ~CommandObjectPlatformSelect () + { + } + + virtual int + HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::PlatformPluginNames (m_interpreter, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + 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); + Error error; + ArchSpec platform_arch; + PlatformSP platform_sp (m_platform_options.CreatePlatformWithOptions (m_interpreter, ArchSpec(), select, error, platform_arch)); + if (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.", + NULL, + 0) + { + } + + virtual + ~CommandObjectPlatformList () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Stream &ostrm = result.GetOutputStream(); + ostrm.Printf("Available platforms:\n"); + + PlatformSP host_platform_sp (Platform::GetDefaultPlatform()); + ostrm.Printf ("%s: %s\n", + host_platform_sp->GetPluginName().GetCString(), + host_platform_sp->GetDescription()); + + uint32_t idx; + for (idx = 0; 1; ++idx) + { + const char *plugin_name = PluginManager::GetPlatformPluginNameAtIndex (idx); + if (plugin_name == NULL) + break; + const char *plugin_desc = PluginManager::GetPlatformPluginDescriptionAtIndex (idx); + if (plugin_desc == NULL) + 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 currently selected platform.", + NULL, + 0) + { + } + + virtual + ~CommandObjectPlatformStatus () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Stream &ostrm = result.GetOutputStream(); + + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + if (platform_sp) + { + platform_sp->GetStatus (ostrm); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendError ("no platform us currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform connect " +//---------------------------------------------------------------------- +class CommandObjectPlatformConnect : public CommandObjectParsed +{ +public: + CommandObjectPlatformConnect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform connect", + "Connect a platform by name to be the currently selected platform.", + "platform connect ", + 0) + { + } + + virtual + ~CommandObjectPlatformConnect () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Stream &ostrm = result.GetOutputStream(); + + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + Error error (platform_sp->ConnectRemote (args)); + if (error.Success()) + { + platform_sp->GetStatus (ostrm); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("%s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("no platform us currently selected\n"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + +//---------------------------------------------------------------------- +// "platform disconnect" +//---------------------------------------------------------------------- +class CommandObjectPlatformDisconnect : public CommandObjectParsed +{ +public: + CommandObjectPlatformDisconnect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "platform disconnect", + "Disconnect a platform by name to be the currently selected platform.", + "platform disconnect", + 0) + { + } + + virtual + ~CommandObjectPlatformDisconnect () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + PlatformSP platform_sp (m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform()); + if (platform_sp) + { + if (args.GetArgumentCount() == 0) + { + Error 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 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", + eFlagRequiresTarget | eFlagTryTargetAPILock), + m_options (interpreter) + { + } + + virtual + ~CommandObjectPlatformProcessLaunch () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + + if (platform_sp) + { + Error 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(); + char exe_path[PATH_MAX]; + if (m_options.launch_info.GetExecutableFile ().GetPath (exe_path, sizeof(exe_path))) + 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 = m_interpreter.GetDebugger(); + + if (argc == 0) + target->GetRunArguments(m_options.launch_info.GetArguments()); + + ProcessSP process_sp (platform_sp->DebugProcess (m_options.launch_info, + debugger, + target, + debugger.GetListener(), + 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" +//---------------------------------------------------------------------- +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 (interpreter) + { + } + + virtual + ~CommandObjectPlatformProcessList () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + + if (platform_sp) + { + Error 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, platform_sp.get(), m_options.show_args, m_options.verbose); + proc_info.DumpAsTableRow(ostrm, platform_sp.get(), 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 = NULL; + const char *match_name = m_options.match_info.GetProcessInfo().GetName(); + if (match_name && match_name[0]) + { + switch (m_options.match_info.GetNameMatchType()) + { + case eNameMatchIgnore: break; + case eNameMatchEquals: match_desc = "matched"; break; + case eNameMatchContains: match_desc = "contained"; break; + case eNameMatchStartsWith: match_desc = "started with"; break; + case eNameMatchEndsWith: match_desc = "ended with"; break; + case eNameMatchRegularExpression: 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, platform_sp.get(), m_options.show_args, m_options.verbose); + for (uint32_t i=0; i [ ...]", + 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); + } + + virtual + ~CommandObjectPlatformProcessInfo () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + PlatformSP platform_sp; + if (target) + { + platform_sp = target->GetPlatform(); + } + if (!platform_sp) + { + platform_sp = m_interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + } + + if (platform_sp) + { + const size_t argc = args.GetArgumentCount(); + if (argc > 0) + { + Error error; + + if (platform_sp->IsConnected()) + { + Stream &ostrm = result.GetOutputStream(); + bool success; + for (size_t i=0; iGetProcessInfo (pid, proc_info)) + { + ostrm.Printf ("Process information for process %" PRIu64 ":\n", pid); + proc_info.Dump (ostrm, platform_sp.get()); + } + else + { + ostrm.Printf ("error: no process information is available for process %" PRIu64 "\n", pid); + } + ostrm.EOL(); + } + else + { + result.AppendErrorWithFormat ("invalid process ID argument '%s'", arg); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + 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(); + } +}; + + + + +class CommandObjectPlatformProcess : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectPlatformProcess (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "platform process", + "A set of commands to query, launch and attach to platform processes", + "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))); + + } + + virtual + ~CommandObjectPlatformProcess () + { + } + +private: + //------------------------------------------------------------------ + // For CommandObjectPlatform only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatformProcess); +}; + + +class CommandObjectPlatformShell : public CommandObjectRaw +{ +public: + CommandObjectPlatformShell (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "platform shell", + "Run a shell command on a the selected platform.", + "platform shell ", + 0) + { + } + + virtual + ~CommandObjectPlatformShell () + { + } + +protected: + virtual bool + DoExecute (const char *raw_command_line, CommandReturnObject &result) + { + // TODO: Implement "Platform::RunShellCommand()" and switch over to using + // the current platform when it is in the interface. + const char *working_dir = NULL; + std::string output; + int status = -1; + int signo = -1; + Error error (Host::RunShellCommand (raw_command_line, working_dir, &status, &signo, &output, 10)); + if (!output.empty()) + result.GetOutputStream().PutCString(output.c_str()); + 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); + } + + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + else + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + return true; + } +}; + +//---------------------------------------------------------------------- +// CommandObjectPlatform constructor +//---------------------------------------------------------------------- +CommandObjectPlatform::CommandObjectPlatform(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "platform", + "A set of 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 ("process", CommandObjectSP (new CommandObjectPlatformProcess (interpreter))); + LoadSubCommand ("shell", CommandObjectSP (new CommandObjectPlatformShell (interpreter))); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectPlatform::~CommandObjectPlatform() +{ +} diff --git a/source/Commands/CommandObjectPlatform.h b/source/Commands/CommandObjectPlatform.h new file mode 100644 index 00000000000..f3bd7584864 --- /dev/null +++ b/source/Commands/CommandObjectPlatform.h @@ -0,0 +1,40 @@ +//===-- CommandObjectPlatform.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectPlatform_h_ +#define liblldb_CommandObjectPlatform_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectPlatform +//------------------------------------------------------------------------- + +class CommandObjectPlatform : public CommandObjectMultiword +{ +public: + CommandObjectPlatform(CommandInterpreter &interpreter); + + virtual + ~CommandObjectPlatform(); + + private: + DISALLOW_COPY_AND_ASSIGN (CommandObjectPlatform); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectPlatform_h_ diff --git a/source/Commands/CommandObjectPlugin.cpp b/source/Commands/CommandObjectPlugin.cpp new file mode 100644 index 00000000000..1bc7632e298 --- /dev/null +++ b/source/Commands/CommandObjectPlugin.cpp @@ -0,0 +1,122 @@ +//===-- CommandObjectPlugin.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectPlugin.h" + +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.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 +{ +private: +public: + CommandObjectPluginLoad (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "plugin load", + "Import a dylib that implements an LLDB plugin.", + NULL) + { + 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 () + { + } + + int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + typedef void (*LLDBCommandPluginInit) (lldb::SBDebugger debugger); + + size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendError ("'plugin load' requires one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char* path = command.GetArgumentAtIndex(0); + + Error error; + + FileSpec dylib_fspec(path,true); + + if (m_interpreter.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", + "A set of commands for managing or customizing plugin commands.", + "plugin []") +{ + LoadSubCommand ("load", CommandObjectSP (new CommandObjectPluginLoad (interpreter))); +} + +CommandObjectPlugin::~CommandObjectPlugin () +{ +} diff --git a/source/Commands/CommandObjectPlugin.h b/source/Commands/CommandObjectPlugin.h new file mode 100644 index 00000000000..9d0f0fcc1ed --- /dev/null +++ b/source/Commands/CommandObjectPlugin.h @@ -0,0 +1,36 @@ +//===-- CommandObjectPlugin.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectPlugin_h_ +#define liblldb_CommandObjectPlugin_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + + class CommandObjectPlugin : public CommandObjectMultiword + { + public: + CommandObjectPlugin (CommandInterpreter &interpreter); + + virtual + ~CommandObjectPlugin (); + }; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectPlugin_h_ diff --git a/source/Commands/CommandObjectProcess.cpp b/source/Commands/CommandObjectProcess.cpp new file mode 100644 index 00000000000..4c406a4f2aa --- /dev/null +++ b/source/Commands/CommandObjectProcess.cpp @@ -0,0 +1,1945 @@ +//===-- CommandObjectProcess.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectProcess.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Breakpoint.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/BreakpointSite.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Module.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.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" + +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) {} + + virtual ~CommandObjectProcessLaunchOrAttach () {} +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; + Error detach_error (process->Detach(keep_stopped)); + if (detach_error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + process = NULL; + } + else + { + result.AppendErrorWithFormat ("Failed to detach from process: %s\n", detach_error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + Error destroy_error (process->Destroy()); + if (destroy_error.Success()) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + process = NULL; + } + 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.", + NULL, + eFlagRequiresTarget, + "restart"), + m_options (interpreter) + { + 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 () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + Options * + GetOptions () + { + return &m_options; + } + + virtual const char *GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + // No repeat for "process launch"... + return ""; + } + +protected: + bool + DoExecute (Args& launch_args, CommandReturnObject &result) + { + Debugger &debugger = m_interpreter.GetDebugger(); + Target *target = debugger.GetSelectedTarget().get(); + Error error; + // If our listener is NULL, users aren't allows to launch + char filename[PATH_MAX]; + const Module *exe_module = target->GetExecutableModulePointer(); + + if (exe_module == NULL) + { + result.AppendError ("no file in target, create a debug target using the 'target create' command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + StateType state = eStateInvalid; + Process *process = m_exe_ctx.GetProcessPtr(); + + if (!StopProcessIfNecessary(process, state, result)) + return false; + + const char *target_settings_argv0 = target->GetArg0(); + + exe_module->GetFileSpec().GetPath (filename, sizeof(filename)); + + if (target_settings_argv0) + { + m_options.launch_info.GetArguments().AppendArgument (target_settings_argv0); + m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), false); + } + else + { + m_options.launch_info.SetExecutableFile(exe_module->GetPlatformFileSpec(), true); + } + + if (launch_args.GetArgumentCount() == 0) + { + Args target_setting_args; + if (target->GetRunArguments(target_setting_args)) + m_options.launch_info.GetArguments().AppendArguments (target_setting_args); + } + else + { + m_options.launch_info.GetArguments().AppendArguments (launch_args); + + // Save the arguments for subsequent runs in the current target. + target->SetRunArguments (launch_args); + } + + if (target->GetDisableASLR()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableASLR); + + if (target->GetDisableSTDIO()) + m_options.launch_info.GetFlags().Set (eLaunchFlagDisableSTDIO); + + m_options.launch_info.GetFlags().Set (eLaunchFlagDebug); + + Args environment; + target->GetEnvironmentAsArgs (environment); + if (environment.GetArgumentCount() > 0) + m_options.launch_info.GetEnvironmentEntries ().AppendArguments (environment); + + // 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. + bool synchronous_execution = m_interpreter.GetSynchronous (); + + // Finalize the file actions, and if none were given, default to opening + // up a pseudo terminal + const bool default_to_use_pty = true; + m_options.launch_info.FinalizeFileActions (target, default_to_use_pty); + + if (state == eStateConnected) + { + if (m_options.launch_info.GetFlags().Test (eLaunchFlagLaunchInTTY)) + { + result.AppendWarning("can't launch in tty when launching through a remote connection"); + m_options.launch_info.GetFlags().Clear (eLaunchFlagLaunchInTTY); + } + } + + if (!m_options.launch_info.GetArchitecture().IsValid()) + m_options.launch_info.GetArchitecture() = target->GetArchitecture(); + + PlatformSP platform_sp (target->GetPlatform()); + + if (platform_sp && platform_sp->CanDebugProcess ()) + { + process = target->GetPlatform()->DebugProcess (m_options.launch_info, + debugger, + target, + debugger.GetListener(), + error).get(); + } + else + { + const char *plugin_name = m_options.launch_info.GetProcessPluginName(); + process = target->CreateProcess (debugger.GetListener(), plugin_name, NULL).get(); + if (process) + error = process->Launch (m_options.launch_info); + } + + if (process == NULL) + { + result.SetError (error, "failed to launch or debug process"); + return false; + } + + + if (error.Success()) + { + const char *archname = exe_module->GetArchitecture().GetArchitectureName(); + + result.AppendMessageWithFormat ("Process %" PRIu64 " launched: '%s' (%s)\n", process->GetID(), filename, archname); + result.SetDidChangeProcessState (true); + if (m_options.launch_info.GetFlags().Test(eLaunchFlagStopAtEntry) == false) + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + StateType state = process->WaitForProcessToStop (NULL); + + if (state == eStateStopped) + { + error = process->Resume(); + if (error.Success()) + { + if (synchronous_execution) + { + state = process->WaitForProcessToStop (NULL); + const bool must_be_alive = true; + if (!StateIsStoppedState(state, must_be_alive)) + { + result.AppendErrorWithFormat ("process isn't stopped: %s", StateAsCString(state)); + } + result.SetDidChangeProcessState (true); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendErrorWithFormat ("process resume at entry point failed: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("initial process state wasn't stopped: %s", StateAsCString(state)); + result.SetStatus (eReturnStatusFailed); + } + } + } + else + { + result.AppendErrorWithFormat ("process launch failed: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } + +protected: + ProcessLaunchCommandOptions m_options; +}; + + +//#define SET1 LLDB_OPT_SET_1 +//#define SET2 LLDB_OPT_SET_2 +//#define SET3 LLDB_OPT_SET_3 +// +//OptionDefinition +//CommandObjectProcessLaunch::CommandOptions::g_option_table[] = +//{ +//{ SET1 | SET2 | SET3, false, "stop-at-entry", 's', no_argument, NULL, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."}, +//{ SET1 , false, "stdin", 'i', required_argument, NULL, 0, eArgTypeDirectoryName, "Redirect stdin for the process to ."}, +//{ SET1 , false, "stdout", 'o', required_argument, NULL, 0, eArgTypeDirectoryName, "Redirect stdout for the process to ."}, +//{ SET1 , false, "stderr", 'e', required_argument, NULL, 0, eArgTypeDirectoryName, "Redirect stderr for the process to ."}, +//{ SET1 | SET2 | SET3, false, "plugin", 'p', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, +//{ SET2 , false, "tty", 't', optional_argument, NULL, 0, eArgTypeDirectoryName, "Start the process in a terminal. If is specified, look for a terminal whose name contains , else start the process in a new terminal."}, +//{ SET3, false, "no-stdio", 'n', no_argument, NULL, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."}, +//{ SET1 | SET2 | SET3, false, "working-dir", 'w', required_argument, NULL, 0, eArgTypeDirectoryName, "Set the current working directory to when running the inferior."}, +//{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +//}; +// +//#undef SET1 +//#undef SET2 +//#undef SET3 + +//------------------------------------------------------------------------- +// CommandObjectProcessAttach +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessAttach +class CommandObjectProcessAttach : public CommandObjectProcessLaunchOrAttach +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success = false; + switch (short_option) + { + case 'c': + attach_info.SetContinueOnceAttached(true); + break; + + case 'p': + { + lldb::pid_t pid = Args::StringToUInt32 (option_arg, LLDB_INVALID_PROCESS_ID, 0, &success); + if (!success || pid == LLDB_INVALID_PROCESS_ID) + { + error.SetErrorStringWithFormat("invalid process ID '%s'", option_arg); + } + else + { + attach_info.SetProcessID (pid); + } + } + break; + + case 'P': + attach_info.SetProcessPluginName (option_arg); + break; + + case 'n': + attach_info.GetExecutableFile().SetFile(option_arg, false); + 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 () + { + attach_info.Clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual bool + HandleOptionArgumentCompletion (Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + 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... + + const OptionDefinition *opt_defs = GetDefinitions(); + if (opt_defs[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 = NULL; + partial_name = input.GetArgumentAtIndex(opt_arg_pos); + + PlatformSP platform_sp (m_interpreter.GetPlatform (true)); + if (platform_sp) + { + ProcessInstanceInfoList process_infos; + ProcessInstanceInfoMatch match_info; + if (partial_name) + { + match_info.GetProcessInfo().GetExecutableFile().SetFile(partial_name, false); + match_info.SetNameMatchType(eNameMatchStartsWith); + } + 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", + 0, + "attach"), + m_options (interpreter) + { + } + + ~CommandObjectProcessAttach () + { + } + + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.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 == NULL) + { + // If there isn't a current target create one. + TargetSP new_target_sp; + Error error; + + error = m_interpreter.GetDebugger().GetTargetList().CreateTarget (m_interpreter.GetDebugger(), + NULL, + NULL, + false, + NULL, // No platform options + new_target_sp); + target = new_target_sp.get(); + if (target == NULL || error.Fail()) + { + result.AppendError(error.AsCString("Error creating target")); + return false; + } + m_interpreter.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); + } + else + { + if (state != eStateConnected) + { + const char *plugin_name = m_options.attach_info.GetProcessPluginName(); + process = target->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get(); + } + + if (process) + { + Error error; + // If no process info was specified, then use the target executable + // name as the process to attach to by default + if (!m_options.attach_info.ProcessInfoSpecified ()) + { + if (old_exec_module_sp) + m_options.attach_info.GetExecutableFile().GetFilename() = old_exec_module_sp->GetPlatformFileSpec().GetFilename(); + + if (!m_options.attach_info.ProcessInfoSpecified ()) + { + error.SetErrorString ("no process specified, create a target with a file, or specify the --pid or --name command option"); + } + } + + if (error.Success()) + { + error = process->Attach (m_options.attach_info); + + if (error.Success()) + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + else + { + result.AppendErrorWithFormat ("attach failed: %s\n", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + // If we're synchronous, wait for the stopped event and report that. + // Otherwise just return. + // FIXME: in the async case it will now be possible to get to the command + // interpreter with a state eStateAttaching. Make sure we handle that correctly. + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + + if (state == eStateStopped) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("attach failed: process did not stop (no such process or permission problem?)"); + process->Destroy(); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + } + + if (result.Succeeded()) + { + // 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; +}; + + +OptionDefinition +CommandObjectProcessAttach::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "continue",'c', no_argument, NULL, 0, eArgTypeNone, "Immediately continue the process once attached."}, +{ LLDB_OPT_SET_ALL, false, "plugin", 'P', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, +{ LLDB_OPT_SET_1, false, "pid", 'p', required_argument, NULL, 0, eArgTypePid, "The process ID of an existing process to attach to."}, +{ LLDB_OPT_SET_2, false, "name", 'n', required_argument, NULL, 0, eArgTypeProcessName, "The name of the process to attach to."}, +{ LLDB_OPT_SET_2, false, "include-existing", 'i', no_argument, NULL, 0, eArgTypeNone, "Include existing processes when doing attach -w."}, +{ LLDB_OPT_SET_2, false, "waitfor", 'w', no_argument, NULL, 0, eArgTypeNone, "Wait for the process with to launch."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessContinue +//------------------------------------------------------------------------- +#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", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options(interpreter) + { + } + + + ~CommandObjectProcessContinue () + { + } + +protected: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success = false; + switch (short_option) + { + case 'i': + m_ignore = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("invalid value for ignore option: \"%s\", should be a number.", option_arg); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + m_ignore = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + uint32_t m_ignore; + }; + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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(process->GetThreadList().GetSelectedThread()); + 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: + Mutex::Locker locker (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; idxGetThreadList().GetThreadAtIndex(idx)->SetResumeState (eStateRunning); + } + } + + Error error(process->Resume()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); + if (synchronous_execution) + { + state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + 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 () + { + return &m_options; + } + + CommandOptions m_options; + +}; + +OptionDefinition +CommandObjectProcessContinue::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "ignore-count",'i', required_argument, NULL, 0, eArgTypeUnsignedInteger, + "Ignore crossings of the breakpoint (if it exists) for the currently selected thread."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessDetach +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessDetach + +class CommandObjectProcessDetach : public CommandObjectParsed +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': + bool tmp_result; + bool success; + tmp_result = Args::StringToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat("invalid boolean option: \"%s\"", option_arg); + 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 () + { + m_keep_stopped = eLazyBoolCalculate; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + LazyBool m_keep_stopped; + }; + + CommandObjectProcessDetach (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process detach", + "Detach from the current process being debugged.", + "process detach", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched), + m_options(interpreter) + { + } + + ~CommandObjectProcessDetach () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + result.AppendMessageWithFormat ("Detaching from process %" PRIu64 "\n", process->GetID()); + // FIXME: This will be a Command Option: + bool keep_stopped; + if (m_options.m_keep_stopped == eLazyBoolCalculate) + { + // Check the process default: + if (process->GetDetachKeepsStopped()) + keep_stopped = true; + else + keep_stopped = false; + } + else if (m_options.m_keep_stopped == eLazyBoolYes) + keep_stopped = true; + else + keep_stopped = false; + + Error 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; +}; + +OptionDefinition +CommandObjectProcessDetach::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "keep-stopped", 's', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the process should be kept stopped on detach (if possible)." }, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessConnect +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessConnect + +class CommandObjectProcessConnect : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + plugin_name.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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 ", + 0), + m_options (interpreter) + { + } + + ~CommandObjectProcessConnect () + { + } + + + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + + TargetSP target_sp (m_interpreter.GetDebugger().GetSelectedTarget()); + Error error; + Process *process = m_exe_ctx.GetProcessPtr(); + if (process) + { + if (process->IsAlive()) + { + result.AppendErrorWithFormat ("Process %" PRIu64 " is currently being debugged, kill the process before connecting.\n", + process->GetID()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + + if (!target_sp) + { + // If there isn't a current target create one. + + error = m_interpreter.GetDebugger().GetTargetList().CreateTarget (m_interpreter.GetDebugger(), + NULL, + NULL, + false, + NULL, // No platform options + target_sp); + if (!target_sp || error.Fail()) + { + result.AppendError(error.AsCString("Error creating target")); + result.SetStatus (eReturnStatusFailed); + return false; + } + m_interpreter.GetDebugger().GetTargetList().SetSelectedTarget(target_sp.get()); + } + + if (command.GetArgumentCount() == 1) + { + const char *plugin_name = NULL; + if (!m_options.plugin_name.empty()) + plugin_name = m_options.plugin_name.c_str(); + + const char *remote_url = command.GetArgumentAtIndex(0); + process = target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), plugin_name, NULL).get(); + + if (process) + { + error = process->ConnectRemote (&process->GetTarget().GetDebugger().GetOutputStream(), remote_url); + + if (error.Fail()) + { + result.AppendError(error.AsCString("Remote connect failed")); + result.SetStatus (eReturnStatusFailed); + target_sp->DeleteCurrentProcess(); + return false; + } + } + else + { + result.AppendErrorWithFormat ("Unable to find process plug-in for remote URL '%s'.\nPlease specify a process plug-in name with the --plugin option, or specify an object file using the \"file\" command.\n", + remote_url); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendErrorWithFormat ("'%s' takes exactly one argument:\nUsage: %s\n", + m_cmd_name.c_str(), + m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectProcessConnect::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "plugin", 'p', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."}, + { 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessPlugin +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessPlugin + +class CommandObjectProcessPlugin : public CommandObjectProxy +{ +public: + + CommandObjectProcessPlugin (CommandInterpreter &interpreter) : + CommandObjectProxy (interpreter, + "process plugin", + "Send a custom command to the current process plug-in.", + "process plugin ", + 0) + { + } + + ~CommandObjectProcessPlugin () + { + } + + virtual CommandObject * + GetProxyCommandObject() + { + Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process) + return process->GetPluginCommandObject(); + return NULL; + } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessLoad +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessLoad + +class CommandObjectProcessLoad : public CommandObjectParsed +{ +public: + + CommandObjectProcessLoad (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process load", + "Load a shared library into the current process.", + "process load [ ...]", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectProcessLoad () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + for (uint32_t i=0; iGetTarget().GetPlatform()->ResolveRemotePath(image_spec, image_spec); + uint32_t image_token = process->LoadImage(image_spec, error); + if (image_token != LLDB_INVALID_IMAGE_TOKEN) + { + result.AppendMessageWithFormat ("Loading \"%s\"...ok\nImage %u loaded.\n", image_path, image_token); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("failed to load '%s': %s", image_path, error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +//------------------------------------------------------------------------- +// 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 ", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectProcessUnload () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + + const size_t argc = command.GetArgumentCount(); + + for (uint32_t i=0; iUnloadImage(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 process being debugged.", + NULL, + eFlagRequiresProcess | eFlagTryTargetAPILock) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + 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 = Args::StringToSInt32(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 + { + Error 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 process being debugged.", + "process interrupt", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessInterrupt () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("no process to halt"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + bool clear_thread_plans = true; + Error 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 process being debugged.", + "process kill", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched) + { + } + + ~CommandObjectProcessKill () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + result.AppendError ("no process to kill"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (command.GetArgumentCount() == 0) + { + Error error (process->Destroy()); + 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(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessStatus +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessStatus + +class CommandObjectProcessStatus : public CommandObjectParsed +{ +public: + CommandObjectProcessStatus (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "process status", + "Show the current status and location of executing process.", + "process status", + eFlagRequiresProcess | eFlagTryTargetAPILock) + { + } + + ~CommandObjectProcessStatus() + { + } + + + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Stream &strm = result.GetOutputStream(); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + // No need to check "process" for validity as eFlagRequiresProcess 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; + process->GetStatus(strm); + process->GetThreadStatus (strm, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectProcessHandle +//------------------------------------------------------------------------- +#pragma mark CommandObjectProcessHandle + +class CommandObjectProcessHandle : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + OptionParsingStarting (); + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + stop.clear(); + notify.clear(); + pass.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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", + "Show or update what the process and debugger should do with various signals received from the OS.", + NULL), + m_options (interpreter) + { + SetHelpLong ("If no signals are specified, update them all. If no update option is specified, list the current values.\n"); + 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 () + { + } + + Options * + GetOptions () + { + return &m_options; + } + + bool + VerifyCommandOptionValue (const std::string &option, int &real_value) + { + bool okay = true; + + bool success = false; + bool tmp_value = Args::StringToBoolean (option.c_str(), 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 = Args::StringToUInt32 (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, UnixSignals &signals) + { + bool stop; + bool suppress; + bool notify; + + str.Printf ("%-10s ", sig_name); + if (signals.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, UnixSignals &signals) + { + 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.GetSignalNumberFromName (signal_args.GetArgumentAtIndex (i)); + if (signo != LLDB_INVALID_SIGNAL_NUMBER) + PrintSignal (str, signo, signal_args.GetArgumentAtIndex (i), signals); + } + } + else // Print info for ALL signals + { + int32_t signo = signals.GetFirstSignalNumber(); + while (signo != LLDB_INVALID_SIGNAL_NUMBER) + { + PrintSignal (str, signo, signals.GetSignalAsCString (signo), signals); + signo = signals.GetNextSignalNumber (signo); + } + } + } + +protected: + bool + DoExecute (Args &signal_args, CommandReturnObject &result) + { + TargetSP target_sp = m_interpreter.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(); + UnixSignals &signals = process_sp->GetUnixSignals(); + int num_signals_set = 0; + + if (num_args > 0) + { + for (size_t i = 0; i < num_args; ++i) + { + int32_t signo = signals.GetSignalNumberFromName (signal_args.GetArgumentAtIndex (i)); + 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.SetShouldStop (signo, (bool) stop_action); + if (pass_action != -1) + { + bool suppress = ! ((bool) pass_action); + signals.SetShouldSuppress (signo, suppress); + } + if (notify_action != -1) + signals.SetShouldNotify (signo, (bool) notify_action); + ++num_signals_set; + } + else + { + result.AppendErrorWithFormat ("Invalid signal name '%s'\n", signal_args.GetArgumentAtIndex (i)); + } + } + } + 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.GetFirstSignalNumber(); + while (signo != LLDB_INVALID_SIGNAL_NUMBER) + { + if (notify_action != -1) + signals.SetShouldNotify (signo, (bool) notify_action); + if (stop_action != -1) + signals.SetShouldStop (signo, (bool) stop_action); + if (pass_action != -1) + { + bool suppress = ! ((bool) pass_action); + signals.SetShouldSuppress (signo, suppress); + } + signo = signals.GetNextSignalNumber (signo); + } + } + } + } + + PrintSignalInformation (result.GetOutputStream(), signal_args, num_signals_set, signals); + + if (num_signals_set > 0) + result.SetStatus (eReturnStatusSuccessFinishNoResult); + else + result.SetStatus (eReturnStatusFailed); + + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectProcessHandle::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "stop", 's', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the process should be stopped if the signal is received." }, +{ LLDB_OPT_SET_1, false, "notify", 'n', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the debugger should notify the user if the signal is received." }, +{ LLDB_OPT_SET_1, false, "pass", 'p', required_argument, NULL, 0, eArgTypeBoolean, "Whether or not the signal should be passed to the process." }, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordProcess +//------------------------------------------------------------------------- + +CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "process", + "A set of commands for operating on a process.", + "process []") +{ + 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))); +} + +CommandObjectMultiwordProcess::~CommandObjectMultiwordProcess () +{ +} + diff --git a/source/Commands/CommandObjectProcess.h b/source/Commands/CommandObjectProcess.h new file mode 100644 index 00000000000..0aaa74d28a0 --- /dev/null +++ b/source/Commands/CommandObjectProcess.h @@ -0,0 +1,37 @@ +//===-- CommandObjectProcess.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectProcess_h_ +#define liblldb_CommandObjectProcess_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordProcess +//------------------------------------------------------------------------- + +class CommandObjectMultiwordProcess : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordProcess (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordProcess (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectProcess_h_ diff --git a/source/Commands/CommandObjectQuit.cpp b/source/Commands/CommandObjectQuit.cpp new file mode 100644 index 00000000000..d04ecdd9885 --- /dev/null +++ b/source/Commands/CommandObjectQuit.cpp @@ -0,0 +1,99 @@ +//===-- CommandObjectQuit.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectQuit.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectQuit +//------------------------------------------------------------------------- + +CommandObjectQuit::CommandObjectQuit (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, "quit", "Quit out of the LLDB debugger.", "quit") +{ +} + +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() == false) + 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 < 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() == false) + { + // 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.GetData(), true)) + { + result.SetStatus(eReturnStatusFailed); + return false; + } + } + m_interpreter.BroadcastEvent (CommandInterpreter::eBroadcastBitQuitCommandReceived); + result.SetStatus (eReturnStatusQuit); + return true; +} + diff --git a/source/Commands/CommandObjectQuit.h b/source/Commands/CommandObjectQuit.h new file mode 100644 index 00000000000..aab0e26cce5 --- /dev/null +++ b/source/Commands/CommandObjectQuit.h @@ -0,0 +1,46 @@ +//===-- CommandObjectQuit.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectQuit_h_ +#define liblldb_CommandObjectQuit_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectQuit +//------------------------------------------------------------------------- + +class CommandObjectQuit : public CommandObjectParsed +{ +public: + + CommandObjectQuit (CommandInterpreter &interpreter); + + virtual + ~CommandObjectQuit (); + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result); + + bool + ShouldAskForConfirmation (bool& is_a_detach); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectQuit_h_ diff --git a/source/Commands/CommandObjectRegister.cpp b/source/Commands/CommandObjectRegister.cpp new file mode 100644 index 00000000000..ba43f23f34a --- /dev/null +++ b/source/Commands/CommandObjectRegister.cpp @@ -0,0 +1,499 @@ +//===-- CommandObjectRegister.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectRegister.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// "register read" +//---------------------------------------------------------------------- +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.", + NULL, + eFlagRequiresFrame | + eFlagRequiresRegContext | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_option_group (interpreter), + 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(); + + } + + virtual + ~CommandObjectRegisterRead () + { + } + + Options * + GetOptions () + { + 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 = m_command_options.alternate_name; + bool prefix_with_name = !prefix_with_altname; + reg_value.Dump(&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); + 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: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Stream &strm = result.GetOutputStream(); + RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext (); + + const RegisterInfo *reg_info = NULL; + 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; iGetUInt64Value (UINT32_MAX, NULL); + if (set_idx < reg_ctx->GetRegisterSetCount()) + { + if (!DumpRegisterSet (m_exe_ctx, strm, reg_ctx, set_idx)) + { + if (errno) + result.AppendErrorWithFormat ("register read failed with errno: %d\n", errno); + else + result.AppendError ("unknown error while reading registers.\n"); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("invalid register set index: %zu\n", 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 option can't be used when registers names are supplied as arguments\n"); + result.SetStatus (eReturnStatusFailed); + } + else + { + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + // 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 + if (*arg_cstr == '$') + arg_cstr = arg_cstr+1; + reg_info = reg_ctx->GetRegisterInfoByName(arg_cstr); + + 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_cstr); + } + } + } + } + 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) + { + } + + virtual + ~CommandOptions () + { + } + + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + set_indexes.Clear(); + dump_all_sets.Clear(); + alternate_name.Clear(); + } + + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) + { + Error error; + const int short_option = g_option_table[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; + } + + // Options table: Required for subclasses of Options. + + static const OptionDefinition g_option_table[]; + + // 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; +}; + +const OptionDefinition +CommandObjectRegisterRead::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "alternate", 'A', no_argument , NULL, 0, eArgTypeNone , "Display register names using the alternate register name if there is one."}, + { LLDB_OPT_SET_1 , false, "set" , 's', required_argument, NULL, 0, eArgTypeIndex , "Specify which register sets to dump by index."}, + { LLDB_OPT_SET_2 , false, "all" , 'a', no_argument , NULL, 0, eArgTypeNone , "Show all register sets."}, +}; + +uint32_t +CommandObjectRegisterRead::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table)/sizeof(OptionDefinition); +} + + +//---------------------------------------------------------------------- +// "register write" +//---------------------------------------------------------------------- +class CommandObjectRegisterWrite : public CommandObjectParsed +{ +public: + CommandObjectRegisterWrite (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "register write", + "Modify a single register value.", + NULL, + eFlagRequiresFrame | + eFlagRequiresRegContext | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused) + { + 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); + } + + virtual + ~CommandObjectRegisterWrite () + { + } + +protected: + virtual bool + DoExecute(Args& command, CommandReturnObject &result) + { + DataExtractor reg_data; + RegisterContext *reg_ctx = m_exe_ctx.GetRegisterContext (); + + if (command.GetArgumentCount() != 2) + { + result.AppendError ("register write takes exactly 2 arguments: "); + result.SetStatus (eReturnStatusFailed); + } + else + { + const char *reg_name = command.GetArgumentAtIndex(0); + const char *value_str = command.GetArgumentAtIndex(1); + + + // 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 + if (reg_name && *reg_name == '$') + reg_name = reg_name+1; + + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(reg_name); + + if (reg_info) + { + RegisterValue reg_value; + + Error error (reg_value.SetValueFromCString (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, + value_str, + error.AsCString()); + } + else + { + result.AppendErrorWithFormat ("Failed to write register '%s' with value '%s'", + reg_name, + value_str); + } + result.SetStatus (eReturnStatusFailed); + } + else + { + result.AppendErrorWithFormat ("Register not found for '%s'.\n", reg_name); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +//---------------------------------------------------------------------- +// CommandObjectRegister constructor +//---------------------------------------------------------------------- +CommandObjectRegister::CommandObjectRegister(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "register", + "A set of commands to access thread registers.", + "register [read|write] ...") +{ + LoadSubCommand ("read", CommandObjectSP (new CommandObjectRegisterRead (interpreter))); + LoadSubCommand ("write", CommandObjectSP (new CommandObjectRegisterWrite (interpreter))); +} + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegister::~CommandObjectRegister() +{ +} diff --git a/source/Commands/CommandObjectRegister.h b/source/Commands/CommandObjectRegister.h new file mode 100644 index 00000000000..7f856c2de52 --- /dev/null +++ b/source/Commands/CommandObjectRegister.h @@ -0,0 +1,45 @@ +//===-- CommandObjectRegister.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectRegister_h_ +#define liblldb_CommandObjectRegister_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectRegister +//------------------------------------------------------------------------- + +class CommandObjectRegister : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectRegister(CommandInterpreter &interpreter); + + virtual + ~CommandObjectRegister(); + +private: + //------------------------------------------------------------------ + // For CommandObjectRegister only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectRegister); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectRegister_h_ diff --git a/source/Commands/CommandObjectSettings.cpp b/source/Commands/CommandObjectSettings.cpp new file mode 100644 index 00000000000..95cc9b68a8f --- /dev/null +++ b/source/Commands/CommandObjectSettings.cpp @@ -0,0 +1,1208 @@ +//===-- CommandObjectSettings.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectSettings.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" + +using namespace lldb; +using namespace lldb_private; +#include "llvm/ADT/StringRef.h" + +//------------------------------------------------------------------------- +// CommandObjectSettingsSet +//------------------------------------------------------------------------- + +class CommandObjectSettingsSet : public CommandObjectRaw +{ +public: + CommandObjectSettingsSet (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings set", + "Set or change the value of a single debugger setting variable.", + NULL), + m_options (interpreter) + { + 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 ( +"When setting a dictionary or array variable, you can set multiple entries \n\ +at once by giving the values to the set command. For example: \n\ +\n\ +(lldb) settings set target.run-args value1 value2 value3 \n\ +(lldb) settings set target.env-vars MYPATH=~/.:/usr/bin SOME_ENV_VAR=12345 \n\ +\n\ +(lldb) settings show target.run-args \n\ + [0]: 'value1' \n\ + [1]: 'value2' \n\ + [3]: 'value3' \n\ +(lldb) settings show target.env-vars \n\ + 'MYPATH=~/.:/usr/bin'\n\ + 'SOME_ENV_VAR=12345' \n\ +\n\ +Warning: The 'set' command re-sets the entire array or dictionary. If you \n\ +just want to add, remove or update individual values (or add something to \n\ +the end), use one of the other settings sub-commands: append, replace, \n\ +insert-before or insert-after.\n"); + + } + + + virtual + ~CommandObjectSettingsSet () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_global (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'g': + m_global = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized options '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_global = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_global; + }; + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + const size_t argc = input.GetArgumentCount(); + const char *arg = NULL; + int setting_var_idx; + for (setting_var_idx = 1; setting_var_idx < argc; ++setting_var_idx) + { + arg = input.GetArgumentAtIndex(setting_var_idx); + if (arg && arg[0] != '-') + break; // We found our setting variable name index + } + if (cursor_index == setting_var_idx) + { + // Attempting to complete setting variable name + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + } + else + { + arg = input.GetArgumentAtIndex(cursor_index); + + if (arg) + { + if (arg[0] == '-') + { + // Complete option name + } + else + { + // Complete setting value + const char *setting_var_name = input.GetArgumentAtIndex(setting_var_idx); + Error error; + lldb::OptionValueSP value_sp (m_interpreter.GetDebugger().GetPropertyValue(&m_exe_ctx, setting_var_name, false, error)); + if (value_sp) + { + value_sp->AutoComplete (m_interpreter, + completion_str.c_str(), + match_start_point, + max_return_elements, + word_complete, + matches); + } + } + } + } + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + Args cmd_args(command); + + // Process possible options. + if (!ParseOptions (cmd_args, result)) + return false; + + const size_t argc = cmd_args.GetArgumentCount (); + if ((argc < 2) && (!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 == NULL) || (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); + + Error error; + if (m_options.m_global) + { + error = m_interpreter.GetDebugger().SetPropertyValue (NULL, + 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 = m_interpreter.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; +}; + +OptionDefinition +CommandObjectSettingsSet::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_2, false, "global", 'g', no_argument, NULL, 0, eArgTypeNone, "Apply the new value to the global default value." }, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectSettingsShow -- Show current values +//------------------------------------------------------------------------- + +class CommandObjectSettingsShow : public CommandObjectParsed +{ +public: + CommandObjectSettingsShow (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "settings show", + "Show the specified internal debugger setting variable and its value, or show all the currently set variables and their values, if nothing is specified.", + NULL) + { + 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); + } + + virtual + ~CommandObjectSettingsShow () {} + + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + + const size_t argc = args.GetArgumentCount (); + if (argc > 0) + { + for (size_t i=0; i 0) + { + const bool dump_qualified_name = true; + + for (size_t i=0; iGetPropertyAtPath (&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 + { + m_interpreter.GetDebugger().DumpAllDescriptions (m_interpreter, result.GetOutputStream()); + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectSettingsRemove +//------------------------------------------------------------------------- + +class CommandObjectSettingsRemove : public CommandObjectRaw +{ +public: + CommandObjectSettingsRemove (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "settings remove", + "Remove the specified element from an array or dictionary settings variable.", + NULL) + { + 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); + } + + virtual + ~CommandObjectSettingsRemove () {} + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + 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 == NULL) || (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); + + Error error (m_interpreter.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 specified element from an internal debugger settings array or dictionary variable with the specified new value.", + NULL) + { + 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); + } + + + virtual + ~CommandObjectSettingsReplace () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + + Args cmd_args(command); + const char *var_name = cmd_args.GetArgumentAtIndex (0); + if ((var_name == NULL) || (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); + + Error error(m_interpreter.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 value(s) into an internal debugger settings array variable, immediately before the specified element.", + NULL) + { + 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); + } + + virtual + ~CommandObjectSettingsInsertBefore () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + 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 == NULL) || (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); + + Error error(m_interpreter.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 value(s) into an internal debugger settings array variable, immediately after the specified element.", + NULL) + { + 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); + } + + virtual + ~CommandObjectSettingsInsertAfter () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + 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 == NULL) || (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); + + Error error(m_interpreter.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 a new value to the end of an internal debugger settings array, dictionary or string variable.", + NULL) + { + 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); + } + + virtual + ~CommandObjectSettingsAppend () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result) + { + 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 == NULL) || (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); + + Error error(m_interpreter.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", + "Erase all the contents of an internal debugger settings variables; this is only valid for variables with clearable types, i.e. strings, arrays or dictionaries.", + NULL) + { + 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); + } + + virtual + ~CommandObjectSettingsClear () {} + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + + // Attempting to complete variable name + if (cursor_index < 2) + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSettingsNameCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + + return matches.GetSize(); + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishNoResult); + const size_t argc = command.GetArgumentCount (); + + if (argc != 1) + { + result.AppendError ("'setttings clear' takes exactly one argument"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const char *var_name = command.GetArgumentAtIndex (0); + if ((var_name == NULL) || (var_name[0] == '\0')) + { + result.AppendError ("'settings clear' command requires a valid variable name; No value supplied"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + Error error (m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + eVarSetOperationClear, + var_name, + NULL)); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSettings +//------------------------------------------------------------------------- + +CommandObjectMultiwordSettings::CommandObjectMultiwordSettings (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "settings", + "A set of commands for manipulating internal settable debugger variables.", + "settings []") +{ + 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))); +} + +CommandObjectMultiwordSettings::~CommandObjectMultiwordSettings () +{ +} diff --git a/source/Commands/CommandObjectSettings.h b/source/Commands/CommandObjectSettings.h new file mode 100644 index 00000000000..eca7adeea76 --- /dev/null +++ b/source/Commands/CommandObjectSettings.h @@ -0,0 +1,41 @@ +//===-- CommandObjectSettings.h ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSettings_h_ +#define liblldb_CommandObjectSettings_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#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); + + virtual + ~CommandObjectMultiwordSettings (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSettings_h_ diff --git a/source/Commands/CommandObjectSource.cpp b/source/Commands/CommandObjectSource.cpp new file mode 100644 index 00000000000..a08e39352b3 --- /dev/null +++ b/source/Commands/CommandObjectSource.cpp @@ -0,0 +1,925 @@ +//===-- CommandObjectSource.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectSource.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.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/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/Options.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectSourceInfo +//------------------------------------------------------------------------- + +class CommandObjectSourceInfo : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 'l': + start_line = Args::StringToUInt32 (option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + default: + error.SetErrorStringWithFormat("unrecognized short option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + file_spec.Clear(); + file_name.clear(); + start_line = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + FileSpec file_spec; + std::string file_name; + uint32_t start_line; + + }; + +public: + CommandObjectSourceInfo(CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "source info", + "Display information about the source lines from the current executable's debug info.", + "source info []"), + m_options (interpreter) + { + } + + ~CommandObjectSourceInfo () + { + } + + + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.AppendError ("Not yet implemented"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectSourceInfo::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, +{ LLDB_OPT_SET_1, false, "file", 'f', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectSourceList +//------------------------------------------------------------------------- +// CommandObjectSourceList +//------------------------------------------------------------------------- + +class CommandObjectSourceList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + } + + ~CommandOptions () + { + } + + Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + switch (short_option) + { + case 'l': + start_line = Args::StringToUInt32 (option_arg, 0); + if (start_line == 0) + error.SetErrorStringWithFormat("invalid line number: '%s'", option_arg); + break; + + case 'c': + num_lines = Args::StringToUInt32 (option_arg, 0); + if (num_lines == 0) + error.SetErrorStringWithFormat("invalid line count: '%s'", option_arg); + break; + + case 'f': + file_name = option_arg; + break; + + case 'n': + symbol_name = option_arg; + break; + + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + address = Args::StringToAddress(&exe_ctx, 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 () + { + 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(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + static OptionDefinition g_option_table[]; + + // 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 (as specified) based on the current executable's debug info.", + NULL, + eFlagRequiresTarget), + m_options (interpreter) + { + } + + ~CommandObjectSourceList () + { + } + + + Options * + GetOptions () + { + return &m_options; + } + + virtual const char * + GetRepeatCommand (Args ¤t_command_args, uint32_t index) + { + // 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. + size_t num_args = current_command_args.GetArgumentCount(); + bool is_reverse = false; + for (size_t i = 0 ; i < num_args; i++) + { + const char *arg = current_command_args.GetArgumentAtIndex(i); + if (arg && (strcmp(arg, "-r") == 0 || strcmp(arg, "--reverse") == 0)) + { + is_reverse = true; + } + } + if (is_reverse) + { + if (m_reverse_name.empty()) + { + m_reverse_name = m_cmd_name; + m_reverse_name.append (" -r"); + } + return m_reverse_name.c_str(); + } + else + return m_cmd_name.c_str(); + } + +protected: + + struct SourceInfo + { + ConstString function; + LineEntry line_entry; + + SourceInfo (const 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.file == rhs.line_entry.file && + line_entry.line == rhs.line_entry.line; + } + + bool + operator != (const SourceInfo &rhs) const + { + return function != rhs.function || + line_entry.file != rhs.line_entry.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 == NULL) + { + // 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); + SearchFilter target_search_filter (m_exe_ctx.GetTargetSP()); + target_search_filter.Search (m_breakpoint_locations); + } + + result.AppendMessageWithFormat("File: %s\n", start_file.GetPath().c_str()); + return target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, + line_no, + 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, const 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].c_str(), false); + 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, const 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].c_str(), false); + 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) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 0) + { + result.AppendErrorWithFormat("'%s' takes no arguments, only flags.\n", GetCommandName()); + 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) + { + const Address &base_address = sc.symbol->GetAddress(); + 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 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; iResolveFileAddress(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, NULL, 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; ishared_from_this()); + target_search_filter.Search (m_breakpoint_locations); + } + + bool show_fullpaths = true; + bool show_module = true; + bool show_inlined_frames = true; + sc.DumpStopContext(&result.GetOutputStream(), + m_exe_ctx.GetBestExecutionContextScope(), + sc.line_entry.range.GetBaseAddress(), + show_fullpaths, + show_module, + show_inlined_frames); + 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; + + target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, + sc.line_entry.line, + 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); + SearchFilter target_search_filter (target->shared_from_this()); + target_search_filter.Search (m_breakpoint_locations); + } + } + else + m_breakpoint_locations.Clear(); + + 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" + "", // 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.size() > 0) + { + ModuleList matching_modules; + for (size_t i = 0, e = m_options.modules.size(); i < e; ++i) + { + FileSpec module_file_spec(m_options.modules[i].c_str(), false); + 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, + 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 = NULL; + + 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 (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); + SearchFilter 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; + + target->GetSourceManager().DisplaySourceLinesWithLineNumbers (sc.comp_unit, + m_options.start_line, + 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 NULL; + } + CommandOptions m_options; + FileLineResolver m_breakpoint_locations; + std::string m_reverse_name; + +}; + +OptionDefinition +CommandObjectSourceList::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "The number of source lines to display."}, +{ LLDB_OPT_SET_1 | + LLDB_OPT_SET_2 , false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, "Look up the source file in the given shared library."}, +{ LLDB_OPT_SET_ALL, false, "show-breakpoints", 'b', no_argument, NULL, 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', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, "The file from which to display source."}, +{ LLDB_OPT_SET_1 , false, "line", 'l', required_argument, NULL, 0, eArgTypeLineNum, "The line number at which to start the display source."}, +{ LLDB_OPT_SET_2 , false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeSymbol, "The name of a function whose source to display."}, +{ LLDB_OPT_SET_3 , false, "address",'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Lookup the address and display the source information for the corresponding file and line."}, +{ LLDB_OPT_SET_4, false, "reverse", 'r', no_argument, NULL, 0, eArgTypeNone, "Reverse the listing to look backwards from the last displayed block of source."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectMultiwordSource + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSource +//------------------------------------------------------------------------- + +CommandObjectMultiwordSource::CommandObjectMultiwordSource (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "source", + "A set of commands for accessing source file information", + "source []") +{ + // "source info" isn't implemented yet... + //LoadSubCommand ("info", CommandObjectSP (new CommandObjectSourceInfo (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectSourceList (interpreter))); +} + +CommandObjectMultiwordSource::~CommandObjectMultiwordSource () +{ +} + diff --git a/source/Commands/CommandObjectSource.h b/source/Commands/CommandObjectSource.h new file mode 100644 index 00000000000..0daef138586 --- /dev/null +++ b/source/Commands/CommandObjectSource.h @@ -0,0 +1,40 @@ +//===-- CommandObjectSource.h.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSource_h_ +#define liblldb_CommandObjectSource_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Core/STLUtils.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordSource +//------------------------------------------------------------------------- + +class CommandObjectMultiwordSource : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordSource (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordSource (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSource.h_h_ diff --git a/source/Commands/CommandObjectSyntax.cpp b/source/Commands/CommandObjectSyntax.cpp new file mode 100644 index 00000000000..d2021ea3eb1 --- /dev/null +++ b/source/Commands/CommandObjectSyntax.cpp @@ -0,0 +1,113 @@ +//===-- CommandObjectSyntax.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectSyntax.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/Options.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectSyntax +//------------------------------------------------------------------------- + +CommandObjectSyntax::CommandObjectSyntax (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "syntax", + "Shows the correct syntax for a given debugger command.", + "syntax ") +{ + CommandArgumentEntry arg; + CommandArgumentData command_arg; + + // Define the first (and only) variant of this arg. + command_arg.arg_type = eArgTypeCommandName; + command_arg.arg_repetition = eArgRepeatPlain; + + // 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); +} + +CommandObjectSyntax::~CommandObjectSyntax() +{ +} + + +bool +CommandObjectSyntax::DoExecute (Args& command, CommandReturnObject &result) +{ + CommandObject::CommandMap::iterator pos; + CommandObject *cmd_obj; + const size_t argc = command.GetArgumentCount(); + + if (argc > 0) + { + cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex(0)); + bool all_okay = true; + for (size_t i = 1; i < argc; ++i) + { + std::string sub_command = command.GetArgumentAtIndex (i); + if (!cmd_obj->IsMultiwordObject()) + all_okay = false; + else + { + cmd_obj = cmd_obj->GetSubcommandObject(sub_command.c_str()); + if (!cmd_obj) + all_okay = false; + } + } + + if (all_okay && (cmd_obj != NULL)) + { + Stream &output_strm = result.GetOutputStream(); + if (cmd_obj->GetOptions() != NULL) + { + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + output_strm.Printf ("(Try 'help %s' for more information on command options syntax.)\n", + cmd_obj->GetCommandName()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + output_strm.Printf ("\nSyntax: %s\n", cmd_obj->GetSyntax()); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + } + else + { + std::string cmd_string; + command.GetCommandString (cmd_string); + result.AppendErrorWithFormat ("'%s' is not a known command.\n", cmd_string.c_str()); + result.AppendError ("Try 'help' to see a current list of commands."); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError ("Must call 'syntax' with a valid command."); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); +} diff --git a/source/Commands/CommandObjectSyntax.h b/source/Commands/CommandObjectSyntax.h new file mode 100644 index 00000000000..47bf85f8549 --- /dev/null +++ b/source/Commands/CommandObjectSyntax.h @@ -0,0 +1,44 @@ +//===-- CommandObjectSyntax.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectSyntax_h_ +#define liblldb_CommandObjectSyntax_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectSyntax +//------------------------------------------------------------------------- + +class CommandObjectSyntax : public CommandObjectParsed +{ +public: + + CommandObjectSyntax (CommandInterpreter &interpreter); + + virtual + ~CommandObjectSyntax (); + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectSyntax_h_ diff --git a/source/Commands/CommandObjectTarget.cpp b/source/Commands/CommandObjectTarget.cpp new file mode 100644 index 00000000000..dd0e2a0011b --- /dev/null +++ b/source/Commands/CommandObjectTarget.cpp @@ -0,0 +1,5354 @@ +//===-- CommandObjectTarget.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectTarget.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupArchitecture.h" +#include "lldb/Interpreter/OptionGroupBoolean.h" +#include "lldb/Interpreter/OptionGroupFile.h" +#include "lldb/Interpreter/OptionGroupFormat.h" +#include "lldb/Interpreter/OptionGroupVariable.h" +#include "lldb/Interpreter/OptionGroupPlatform.h" +#include "lldb/Interpreter/OptionGroupUInt64.h" +#include "lldb/Interpreter/OptionGroupUUID.h" +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/FuncUnwinders.h" +#include "lldb/Symbol/LineTable.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/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/ThreadSpec.h" + +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, ""); + + strm.Printf ("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, exe_path); + + uint32_t properties = 0; + if (target_arch.IsValid()) + { + strm.Printf ("%sarch=%s", properties++ > 0 ? ", " : " ( ", target_arch.GetTriple().str().c_str()); + 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; + process_sp->GetStatus (strm); + process_sp->GetThreadStatus (strm, + only_threads_with_stop_reason, + start_frame, + num_frames, + num_frames_with_source); + + } +} + +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; iGetExecutableModule()); + 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 (core_file) + { + char core_path[PATH_MAX]; + core_file.GetPath(core_path, sizeof(core_path)); + if (core_file.Exists()) + { + FileSpec core_file_dir; + core_file_dir.GetDirectory() = core_file.GetDirectory(); + target_sp->GetExecutableSearchPaths ().Append (core_file_dir); + + ProcessSP process_sp (target_sp->CreateProcess (m_interpreter.GetDebugger().GetListener(), NULL, &core_file)); + + if (process_sp) + { + // Seems wierd 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-file option.\n", m_cmd_name.c_str()); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + + } + +private: + OptionGroupOptions m_option_group; + OptionGroupArchitecture m_arch_option; + OptionGroupPlatform m_platform_options; + OptionGroupFile m_core_file; + OptionGroupFile m_symbol_file; + OptionGroupFile m_remote_file; + OptionGroupBoolean 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.", + NULL, + 0) + { + } + + virtual + ~CommandObjectTargetList () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + if (args.GetArgumentCount() == 0) + { + Stream &strm = result.GetOutputStream(); + + bool show_stopped_process_status = false; + if (DumpTargetList (m_interpreter.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.", + NULL, + 0) + { + } + + virtual + ~CommandObjectTargetSelect () + { + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + if (args.GetArgumentCount() == 1) + { + bool success = false; + const char *target_idx_arg = args.GetArgumentAtIndex(0); + uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success); + if (success) + { + TargetList &target_list = m_interpreter.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 + { + result.AppendErrorWithFormat ("index %u is out of range, valid target indexes are 0 - %u\n", + target_idx, + num_targets - 1); + 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.", + NULL, + 0), + m_option_group (interpreter), + m_cleanup_option (LLDB_OPT_SET_1, false, "clean", 'c', "Perform extra cleanup to minimize memory consumption after deleting the target.", false, false) + { + m_option_group.Append (&m_cleanup_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetDelete () + { + } + + Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + const size_t argc = args.GetArgumentCount(); + std::vector delete_target_list; + TargetList &target_list = m_interpreter.GetDebugger().GetTargetList(); + bool success = true; + TargetSP target_sp; + 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); + success = false; + } + + for (uint32_t arg_idx = 0; success && arg_idx < argc; ++arg_idx) + { + const char *target_idx_arg = args.GetArgumentAtIndex(arg_idx); + uint32_t target_idx = Args::StringToUInt32 (target_idx_arg, UINT32_MAX, 0, &success); + if (success) + { + 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); + success = false; + } + else + { + result.AppendErrorWithFormat("invalid target index '%s'\n", target_idx_arg); + result.SetStatus (eReturnStatusFailed); + success = false; + } + } + + } + else + { + target_sp = target_list.GetSelectedTarget(); + if (target_sp) + { + delete_target_list.push_back (target_sp); + } + else + { + result.AppendErrorWithFormat("no target is currently selected\n"); + result.SetStatus (eReturnStatusFailed); + success = false; + } + } + if (success) + { + 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 result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupBoolean m_cleanup_option; +}; + + +#pragma mark CommandObjectTargetVariable + +//---------------------------------------------------------------------- +// "target variable" +//---------------------------------------------------------------------- + +class CommandObjectTargetVariable : public CommandObjectParsed +{ +public: + CommandObjectTargetVariable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target variable", + "Read global variable(s) prior to, or while running your binary.", + NULL, + eFlagRequiresTarget), + m_option_group (interpreter), + m_option_variable (false), // Don't include frame options + m_option_format (eFormatDefault), + m_option_compile_units (LLDB_OPT_SET_1, false, "file", '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",'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(); + } + + virtual + ~CommandObjectTargetVariable () + { + } + + void + DumpValueObject (Stream &s, VariableSP &var_sp, ValueObjectSP &valobj_sp, const char *root_name) + { + ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions()); + + 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; + + 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); + + ValueObject::DumpValueObject (s, + valobj_sp.get(), + options); + + } + + + static size_t GetVariableCallback (void *baton, + const char *name, + VariableList &variable_list) + { + Target *target = static_cast(baton); + if (target) + { + return target->GetImages().FindGlobalVariables (ConstString(name), + true, + UINT32_MAX, + variable_list); + } + return 0; + } + + + + Options * + GetOptions () + { + 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; iGetName().GetCString()); + } + } + } + + } + virtual bool + DoExecute (Args& args, CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + const size_t argc = args.GetArgumentCount(); + Stream &s = result.GetOutputStream(); + + if (argc > 0) + { + + 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(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, + true, + UINT32_MAX, + variable_list); + } + else + { + Error 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_idxGetName().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 = NULL; + 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_idxGetImages().FindFirstModule(module_spec)); + if (module_sp) + { + if (num_compile_units > 0) + { + for (size_t cu_idx=0; cu_idxFindCompileUnits(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_idxGetImages().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_idxGetVariableList(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("."); // Any global with at least one character + VariableList variable_list; + sc.module_sp->FindGlobalVariables(all_globals_regex, append, 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.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.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; iGetImageSearchPathList().Append (ConstString(from), + ConstString(to), + last_pair); // Notify if this is the last pair + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + if (from[0]) + result.AppendError (" can't be empty\n"); + else + result.AppendError (" 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 () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.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.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + size_t argc = command.GetArgumentCount(); + // check for at least 3 arguments and an odd nubmer of parameters + if (argc >= 3 && argc & 1) + { + bool success = false; + + uint32_t insert_idx = Args::StringToUInt32(command.GetArgumentAtIndex(0), UINT32_MAX, 0, &success); + + if (!success) + { + result.AppendErrorWithFormat(" 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; iGetImageSearchPathList().Insert (ConstString(from), + ConstString(to), + insert_idx, + last_pair); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + if (from[0]) + result.AppendError (" can't be empty\n"); + else + result.AppendError (" 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 () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.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.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.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) + { + const char *arch_cstr; + if (full_triple) + arch_cstr = module->GetArchitecture().GetTriple().str().c_str(); + else + arch_cstr = module->GetArchitecture().GetArchitectureName(); + if (width) + strm.Printf("%-*s", width, arch_cstr); + else + strm.PutCString(arch_cstr); + } +} + +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, + bool load_addresses) +{ + 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 0) + strm << "\n\n"; + + strm << "Line table for " << *static_cast (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(), + lldb::eDescriptionLevelBrief); + 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 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) + { + uint32_t i; + std::vector match_indexes; + ConstString symbol_name (name); + uint32_t num_matches = 0; + if (name_is_regex) + { + RegularExpression name_regexp(name); + 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 (); + //Symtab::DumpSymbolHeader (&strm); + for (i=0; i < num_matches; ++i) + { + Symbol *symbol = symtab->SymbolAtIndex(match_indexes[i]); + DumpAddress (interpreter.GetExecutionContext().GetBestExecutionContextScope(), + symbol->GetAddress(), + verbose, + strm); + +// strm.Indent (); +// symbol->Dump (&strm, interpreter.GetExecutionContext().GetTargetPtr(), i); + } + strm.IndentLess (); + return num_matches; + } + } + } + } + return 0; +} + + +static void +DumpSymbolContextList (ExecutionContextScope *exe_scope, Stream &strm, SymbolContextList &sc_list, bool verbose) +{ + strm.IndentMore (); + uint32_t i; + const uint32_t num_matches = sc_list.GetSize(); + + for (i=0; iFindFunctions (function_name_regex, + include_symbols, + include_inlines, + append, + sc_list); + } + else + { + ConstString function_name (name); + num_matches = module->FindFunctions (function_name, + NULL, + eFunctionNameTypeAuto, + include_symbols, + include_inlines, + append, + sc_list); + } + + if (num_matches) + { + strm.Indent (); + strm.Printf("%zu match%s found in ", 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; + SymbolContext sc; + + ConstString name(name_cstr); + num_matches = module->FindTypes(sc, name, name_is_fully_qualified, max_num_matches, type_list); + + if (num_matches) + { + strm.Indent (); + strm.Printf("%zu match%s found in ", num_matches, num_matches > 1 ? "es" : ""); + DumpFullpath (strm, &module->GetFileSpec(), 0); + strm.PutCString(":\n"); + const uint32_t num_types = type_list.GetSize(); + for (uint32_t i=0; iGetClangFullType (); + 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->GetClangFullType (); + 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, + const SymbolContext &sym_ctx, + const char *name_cstr, + bool name_is_regex) +{ + if (!sym_ctx.module_sp) + return 0; + + 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); + num_matches = sym_ctx.module_sp->FindTypes(sym_ctx, name, name_is_fully_qualified, max_num_matches, type_list); + + if (num_matches) + { + strm.Indent (); + strm.PutCString("Best match found in "); + DumpFullpath (strm, &sym_ctx.module_sp->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->GetClangFullType (); + 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->GetClangFullType (); + 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) +{ +// Dump specified images (by basename or fullpath) + FileSpec module_file_spec(module_name, false); + ModuleSpec module_spec (module_file_spec); + + const size_t initial_size = module_list.GetSize (); + + if (check_global_list) + { + // Check the global list + Mutex::Locker locker(Module::GetAllocationModuleCollectionMutex()); + const size_t num_modules = Module::GetNumberAllocatedModules(); + ModuleSP module_sp; + for (size_t image_idx = 0; image_idxMatchesModuleSpec (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); + } + + virtual + ~CommandObjectTargetModulesModuleAutoComplete () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + // Arguments are the standard module completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eModuleCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } +}; + +#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); + } + + virtual + ~CommandObjectTargetModulesSourceFileAutoComplete () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + // Arguments are the standard source file completer. + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eSourceFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } +}; + + +#pragma mark CommandObjectTargetModulesDumpSymtab + + +class CommandObjectTargetModulesDumpSymtab : public CommandObjectTargetModulesModuleAutoComplete +{ +public: + CommandObjectTargetModulesDumpSymtab (CommandInterpreter &interpreter) : + CommandObjectTargetModulesModuleAutoComplete (interpreter, + "target modules dump symtab", + "Dump the symbol table from one or more target modules.", + NULL), + m_options (interpreter) + { + } + + virtual + ~CommandObjectTargetModulesDumpSymtab () + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_sort_order (eSortOrderNone) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 's': + m_sort_order = (SortOrder) Args::StringToOptionEnum (option_arg, + g_option_table[option_idx].enum_values, + eSortOrderNone, + error); + break; + + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_sort_order = eSortOrderNone; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + static OptionDefinition g_option_table[]; + + SortOrder m_sort_order; + }; + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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 + Mutex::Locker modules_locker(target->GetImages().GetMutex()); + const size_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping symbol table for %zu modules.\n", num_modules); + for (size_t image_idx = 0; image_idx 0) + { + result.GetOutputStream().EOL(); + result.GetOutputStream().EOL(); + } + 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)) != NULL; ++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 0) + { + result.GetOutputStream().EOL(); + result.GetOutputStream().EOL(); + } + 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; +}; + +static OptionEnumValueElement +g_sort_option_enumeration[4] = +{ + { eSortOrderNone, "none", "No sorting, use the original symbol table order."}, + { eSortOrderByAddress, "address", "Sort output by symbol address."}, + { eSortOrderByName, "name", "Sort output by symbol name."}, + { 0, NULL, NULL } +}; + + +OptionDefinition +CommandObjectTargetModulesDumpSymtab::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "sort", 's', required_argument, g_sort_option_enumeration, 0, eArgTypeSortOrder, "Supply a sort order when dumping the symbol table."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#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 [ ...]") + NULL) + { + } + + virtual + ~CommandObjectTargetModulesDumpSections () + { + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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 %zu modules.\n", num_modules); + for (size_t image_idx = 0; image_idxGetImages().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)) != NULL; ++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 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no matching executable images found"); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +#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 [ ...]") + NULL) + { + } + + virtual + ~CommandObjectTargetModulesDumpSymfile () + { + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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(); + Mutex::Locker modules_locker (target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules > 0) + { + result.GetOutputStream().Printf("Dumping debug symbols for %zu modules.\n", num_modules); + for (uint32_t image_idx = 0; image_idx 0) + { + for (size_t i=0; i 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.", + NULL, + eFlagRequiresTarget) + { + } + + virtual + ~CommandObjectTargetModulesDumpLineTable () + { + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + 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.AppendErrorWithFormat ("\nSyntax: %s\n", m_cmd_syntax.c_str()); + result.SetStatus (eReturnStatusFailed); + } + else + { + // Dump specified images (by basename or fullpath) + const char *arg_cstr; + for (int arg_idx = 0; (arg_cstr = command.GetArgumentAtIndex(arg_idx)) != NULL; ++arg_idx) + { + FileSpec file_spec(arg_cstr, false); + + const ModuleList &target_modules = target->GetImages(); + Mutex::Locker modules_locker(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 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + { + result.AppendError ("no source filenames matched any command arguments"); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } +}; + + +#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", + "A set of commands for dumping information about one or more target modules.", + "target modules dump [symtab|sections|symfile|line-table] [ ...]") + { + LoadSubCommand ("symtab", CommandObjectSP (new CommandObjectTargetModulesDumpSymtab (interpreter))); + LoadSubCommand ("sections", CommandObjectSP (new CommandObjectTargetModulesDumpSections (interpreter))); + LoadSubCommand ("symfile", CommandObjectSP (new CommandObjectTargetModulesDumpSymfile (interpreter))); + LoadSubCommand ("line-table", CommandObjectSP (new CommandObjectTargetModulesDumpLineTable (interpreter))); + } + + virtual + ~CommandObjectTargetModulesDump() + { + } +}; + +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 []"), + m_option_group (interpreter), + 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(); + } + + virtual + ~CommandObjectTargetModulesAdd () + { + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + +protected: + + OptionGroupOptions m_option_group; + OptionGroupUUID m_uuid_option_group; + OptionGroupFile m_symbol_file; + + + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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->GetSharedModule (module_spec)); + if (module_sp) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + return true; + } + else + { + flush = true; + + 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.GetString().c_str(), + 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.GetString().c_str(), + module_spec.GetFileSpec().GetPath().c_str()); + } + } + else + { + result.AppendErrorWithFormat ("Unable to create the executable or symbol file with UUID %s", + strm.GetString().c_str()); + } + 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.GetString().c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError ("one or more executable image paths must be specified"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + else + { + for (size_t i=0; iGetSharedModule (module_spec, &error)); + if (!module_sp) + { + const char *error_cstr = error.AsCString(); + if (error_cstr) + result.AppendError (error_cstr); + else + result.AppendErrorWithFormat ("unsupported module: %s", path); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + flush = true; + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + char resolved_path[PATH_MAX]; + result.SetStatus (eReturnStatusFailed); + if (file_spec.GetPath (resolved_path, sizeof(resolved_path))) + { + if (strcmp (resolved_path, path) != 0) + { + result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", path, resolved_path); + break; + } + } + result.AppendErrorWithFormat ("invalid module path '%s'\n", path); + 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 --uuid ]
[
....]"), + m_option_group (interpreter), + m_file_option (LLDB_OPT_SET_1, false, "file", 'f', 0, eArgTypeFilename, "Fullpath or basename for module to load."), + 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_slide_option, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectTargetModulesLoad () + { + } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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; + if (m_file_option.GetOptionValue().OptionWasSet()) + { + search_using_module_spec = true; + module_spec.GetFileSpec() = m_file_option.GetOptionValue().GetCurrentValue(); + } + + 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(); + module->SetLoadAddress (*target, slide, 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 \" option can't be used in conjunction with setting section load addresses.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + for (size_t i=0; iFindSectionByName(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(); + } + } + 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; iGetFileSpec().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 \" or the \"--uuid \" option must be specified.\n"); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + return result.Succeeded(); + } + + OptionGroupOptions m_option_group; + OptionGroupUUID m_uuid_option_group; + OptionGroupFile m_file_option; + OptionGroupUInt64 m_slide_option; +}; + +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectTargetModulesList : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_format_array(), + m_use_global_module_list (false), + m_module_addr (LLDB_INVALID_ADDRESS) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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') + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_module_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + else + { + unsigned long width = 0; + if (option_arg) + width = strtoul (option_arg, NULL, 0); + m_format_array.push_back(std::make_pair(short_option, width)); + } + return error; + } + + void + OptionParsingStarting () + { + m_format_array.clear(); + m_use_global_module_list = false; + m_module_addr = LLDB_INVALID_ADDRESS; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + typedef std::vector< std::pair > 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 []"), + m_options (interpreter) + { + } + + virtual + ~CommandObjectTargetModulesList () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.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 == NULL && use_global_module_list == false) + { + 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; + Mutex::Locker locker; // This locker will be locked on the mutex in module_list_ptr if it is non-NULL. + // Otherwise it will lock the AllocationModuleCollectionMutex when accessing + // the global module list directly. + const ModuleList *module_list_ptr = NULL; + const size_t argc = command.GetArgumentCount(); + if (argc == 0) + { + if (use_global_module_list) + { + locker.Lock (Module::GetAllocationModuleCollectionMutex()); + num_modules = Module::GetNumberAllocatedModules(); + } + else + { + module_list_ptr = &target->GetImages(); + } + } + else + { + for (size_t i=0; iGetMutex()); + num_modules = module_list_ptr->GetSize(); + } + + if (num_modules > 0) + { + for (uint32_t image_idx = 0; image_idxGetModuleAtIndexUnlocked(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 == NULL) + { + 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; iGetFileSpec(), 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 header_addr(objfile->GetHeaderAddress()); + if (header_addr.IsValid()) + { + if (target && !target->GetSectionLoadList().IsEmpty()) + { + lldb::addr_t header_load_addr = header_addr.GetLoadAddress (target); + if (header_load_addr == LLDB_INVALID_ADDRESS) + { + header_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, header_load_addr - header_addr.GetFileAddress()); + } + else + { + // Show the load address of the image + strm.Printf ("0x%*.*" PRIx64, addr_nibble_width, addr_nibble_width, header_load_addr); + } + } + break; + } + // The address was valid, but the image isn't loaded, output the address in an appropriate format + header_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("{%*zu}", width, ref_count); + else + strm.Printf("{%zu}", ref_count); + } + break; + + case 's': + case 'S': + { + SymbolVendor *symbol_vendor = module->GetSymbolVendor(); + if (symbol_vendor) + { + SymbolFile *symbol_file = symbol_vendor->GetSymbolFile(); + if (symbol_file) + { + if (format_char == 'S') + { + FileSpec &symfile_spec = symbol_file->GetObjectFile()->GetFileSpec(); + // 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, &symbol_file->GetObjectFile()->GetFileSpec(), width); + dump_object_name = true; + break; + } + } + strm.Printf("%.*s", width, ""); + } + break; + + case 'm': + module->GetModificationTime().Dump(&strm, width); + break; + + case 'p': + strm.Printf("%p", 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; +}; + +OptionDefinition +CommandObjectTargetModulesList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Display the image at this address."}, + { LLDB_OPT_SET_1, false, "arch", 'A', optional_argument, NULL, 0, eArgTypeWidth, "Display the architecture when listing images."}, + { LLDB_OPT_SET_1, false, "triple", 't', optional_argument, NULL, 0, eArgTypeWidth, "Display the triple when listing images."}, + { LLDB_OPT_SET_1, false, "header", 'h', no_argument, NULL, 0, eArgTypeNone, "Display the image header address as a load address if debugging, a file address otherwise."}, + { LLDB_OPT_SET_1, false, "offset", 'o', no_argument, NULL, 0, eArgTypeNone, "Display the image header address offset from the header file address (the slide amount)."}, + { LLDB_OPT_SET_1, false, "uuid", 'u', no_argument, NULL, 0, eArgTypeNone, "Display the UUID when listing images."}, + { LLDB_OPT_SET_1, false, "fullpath", 'f', optional_argument, NULL, 0, eArgTypeWidth, "Display the fullpath to the image object file."}, + { LLDB_OPT_SET_1, false, "directory", 'd', optional_argument, NULL, 0, eArgTypeWidth, "Display the directory with optional width for the image object file."}, + { LLDB_OPT_SET_1, false, "basename", 'b', optional_argument, NULL, 0, eArgTypeWidth, "Display the basename with optional width for the image object file."}, + { LLDB_OPT_SET_1, false, "symfile", 's', optional_argument, NULL, 0, eArgTypeWidth, "Display the fullpath to the image symbol file with optional width."}, + { LLDB_OPT_SET_1, false, "symfile-unique", 'S', optional_argument, NULL, 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', optional_argument, NULL, 0, eArgTypeWidth, "Display the modification time with optional width of the module."}, + { LLDB_OPT_SET_1, false, "ref-count", 'r', optional_argument, NULL, 0, eArgTypeWidth, "Display the reference count if the module is still in the shared module cache."}, + { LLDB_OPT_SET_1, false, "pointer", 'p', optional_argument, NULL, 0, eArgTypeNone, "Display the module pointer."}, + { LLDB_OPT_SET_1, false, "global", 'g', no_argument, NULL, 0, eArgTypeNone, "Display the modules from the global module list, not just the current target."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#pragma mark CommandObjectTargetModulesShowUnwind + +//---------------------------------------------------------------------- +// Lookup unwind information in images +//---------------------------------------------------------------------- + +class CommandObjectTargetModulesShowUnwind : public CommandObjectParsed +{ +public: + + enum + { + eLookupTypeInvalid = -1, + eLookupTypeAddress = 0, + eLookupTypeSymbol, + eLookupTypeFunction, + eLookupTypeFunctionOrSymbol, + kNumLookupTypes + }; + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_type(eLookupTypeInvalid), + m_str(), + m_addr(LLDB_INVALID_ADDRESS) + { + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_type = eLookupTypeAddress; + m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + if (m_addr == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("invalid address string '%s'", option_arg); + break; + } + + case 'n': + { + m_str = option_arg; + m_type = eLookupTypeFunctionOrSymbol; + break; + } + } + + return error; + } + + void + OptionParsingStarting () + { + m_type = eLookupTypeInvalid; + m_str.clear(); + m_addr = LLDB_INVALID_ADDRESS; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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.", + NULL, + eFlagRequiresTarget | + eFlagRequiresProcess | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + } + + virtual + ~CommandObjectTargetModulesShowUnwind () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_exe_ctx.GetTargetPtr(); + Process *process = m_exe_ctx.GetProcessPtr(); + ABI *abi = NULL; + if (process) + abi = process->GetABI().get(); + + if (process == NULL) + { + 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.get() == NULL) + { + 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); + } + } + } + + size_t num_matches = sc_list.GetSize(); + for (uint32_t idx = 0; idx < num_matches; idx++) + { + SymbolContext sc; + sc_list.GetContextAtIndex(idx, sc); + if (sc.symbol == NULL && sc.function == NULL) + continue; + if (sc.module_sp.get() == NULL || sc.module_sp->GetObjectFile() == NULL) + 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->GetObjectFile()->GetUnwindTable().GetUncachedFuncUnwindersContainingAddress(start_addr, sc)); + if (func_unwinders_sp.get() == NULL) + continue; + + Address first_non_prologue_insn (func_unwinders_sp->GetFirstNonPrologueInsn(*target)); + if (first_non_prologue_insn.IsValid()) + { + result.GetOutputStream().Printf("First non-prologue instruction is at address 0x%" PRIx64 " or offset %" PRId64 " into the function.\n", first_non_prologue_insn.GetLoadAddress(target), first_non_prologue_insn.GetLoadAddress(target) - start_addr); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP non_callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtNonCallSite(*thread.get()); + if (non_callsite_unwind_plan.get()) + { + result.GetOutputStream().Printf("Asynchronous (not restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + non_callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP callsite_unwind_plan = func_unwinders_sp->GetUnwindPlanAtCallSite(-1); + if (callsite_unwind_plan.get()) + { + result.GetOutputStream().Printf("Synchronous (restricted to call-sites) UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + callsite_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP arch_default_unwind_plan = func_unwinders_sp->GetUnwindPlanArchitectureDefault(*thread.get()); + if (arch_default_unwind_plan.get()) + { + result.GetOutputStream().Printf("Architecture default UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + arch_default_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + UnwindPlanSP fast_unwind_plan = func_unwinders_sp->GetUnwindPlanFastUnwind(*thread.get()); + if (fast_unwind_plan.get()) + { + result.GetOutputStream().Printf("Fast UnwindPlan for %s`%s (start addr 0x%" PRIx64 "):\n", sc.module_sp->GetPlatformFileSpec().GetFilename().AsCString(), funcname.AsCString(), start_addr); + fast_unwind_plan->Dump(result.GetOutputStream(), thread.get(), LLDB_INVALID_ADDRESS); + result.GetOutputStream().Printf ("\n"); + } + + + result.GetOutputStream().Printf ("\n"); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetModulesShowUnwind::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "name", 'n', required_argument, NULL, 0, eArgTypeFunctionName, "Show unwind instructions for a function or symbol name."}, + { LLDB_OPT_SET_2, false, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Show unwind instructions for a function or symbol containing an address"}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//---------------------------------------------------------------------- +// Lookup information in images +//---------------------------------------------------------------------- +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 (CommandInterpreter &interpreter) : + Options(interpreter) + { + OptionParsingStarting(); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + m_type = eLookupTypeAddress; + ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); + m_addr = Args::StringToAddress(&exe_ctx, option_arg, LLDB_INVALID_ADDRESS, &error); + } + break; + + case 'o': + m_offset = Args::StringToUInt64(option_arg, LLDB_INVALID_ADDRESS); + if (m_offset == LLDB_INVALID_ADDRESS) + error.SetErrorStringWithFormat ("invalid offset string '%s'", option_arg); + break; + + case 's': + m_str = option_arg; + m_type = eLookupTypeSymbol; + break; + + case 'f': + m_file.SetFile (option_arg, false); + m_type = eLookupTypeFileLine; + break; + + case 'i': + m_include_inlines = false; + break; + + case 'l': + m_line_number = Args::StringToUInt32(option_arg, UINT32_MAX); + if (m_line_number == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid line number string '%s'", option_arg); + 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 = 1; + break; + + case 'A': + m_print_all = true; + break; + + case 'r': + m_use_regex = true; + break; + } + + return error; + } + + void + OptionParsingStarting () + { + 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; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + 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.", + NULL, + eFlagRequiresTarget), + m_options (interpreter) + { + 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); + } + + virtual + ~CommandObjectTargetModulesLookup () + { + } + + virtual Options * + GetOptions () + { + 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, + 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_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); + syntax_error = true; + break; + } + + result.SetStatus (eReturnStatusFailed); + return false; + } + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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(); + Mutex::Locker modules_locker(target_modules.GetMutex()); + const size_t num_modules = target_modules.GetSize(); + if (num_modules > 0) + { + for (i = 0; i 0) + { + for (size_t j=0; j 0) + result.SetStatus (eReturnStatusSuccessFinishResult); + else + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetModulesLookup::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, true, "address", 'a', required_argument, NULL, 0, eArgTypeAddressOrExpression, "Lookup an address in one or more target modules."}, + { LLDB_OPT_SET_1, false, "offset", 'o', required_argument, NULL, 0, eArgTypeOffset, "When looking up an address subtract from any addresses before doing the lookup."}, + { LLDB_OPT_SET_2| LLDB_OPT_SET_4 | LLDB_OPT_SET_5 + /* FIXME: re-enable this for types when the LookupTypeInModule actually uses the regex option: | LLDB_OPT_SET_6 */ , + false, "regex", 'r', no_argument, NULL, 0, eArgTypeNone, "The argument for name lookups are regular expressions."}, + { LLDB_OPT_SET_2, true, "symbol", 's', required_argument, NULL, 0, eArgTypeSymbol, "Lookup a symbol by name in the symbol tables in one or more target modules."}, + { LLDB_OPT_SET_3, true, "file", 'f', required_argument, NULL, 0, eArgTypeFilename, "Lookup a file by fullpath or basename in one or more target modules."}, + { LLDB_OPT_SET_3, false, "line", 'l', required_argument, NULL, 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', no_argument, NULL, 0, eArgTypeNone, "Ignore inline entries (must be used in conjunction with --file or --function)."}, + { LLDB_OPT_SET_4, true, "function", 'F', required_argument, NULL, 0, eArgTypeFunctionName, "Lookup a function by name in the debug symbols in one or more target modules."}, + { LLDB_OPT_SET_5, true, "name", 'n', required_argument, NULL, 0, eArgTypeFunctionOrSymbol, "Lookup a function or symbol by name in one or more target modules."}, + { LLDB_OPT_SET_6, true, "type", 't', required_argument, NULL, 0, eArgTypeName, "Lookup a type by name in the debug symbols in one or more target modules."}, + { LLDB_OPT_SET_ALL, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, "Enable verbose lookup information."}, + { LLDB_OPT_SET_ALL, false, "all", 'A', no_argument, NULL, 0, eArgTypeNone, "Print all matches, not just the best match, if a best match is available."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +#pragma mark CommandObjectMultiwordImageSearchPaths + +//------------------------------------------------------------------------- +// CommandObjectMultiwordImageSearchPaths +//------------------------------------------------------------------------- + +class CommandObjectTargetModulesImageSearchPaths : public CommandObjectMultiword +{ +public: + + CommandObjectTargetModulesImageSearchPaths (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target modules search-paths", + "A set of commands for operating on debugger target image search paths.", + "target modules search-paths []") + { + 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() + { + } +}; + + + +#pragma mark CommandObjectTargetModules + +//------------------------------------------------------------------------- +// CommandObjectTargetModules +//------------------------------------------------------------------------- + +class CommandObjectTargetModules : public CommandObjectMultiword +{ +public: + //------------------------------------------------------------------ + // Constructors and Destructors + //------------------------------------------------------------------ + CommandObjectTargetModules(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target modules", + "A set of commands for accessing information for one or more target modules.", + "target modules ...") + { + 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))); + + } + virtual + ~CommandObjectTargetModules() + { + } + +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 []", eFlagRequiresTarget), + m_option_group (interpreter), + 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(); + } + + virtual + ~CommandObjectTargetSymbolsAdd () + { + } + + virtual int + HandleArgumentCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + OptionElementVector &opt_element_vector, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) + { + std::string completion_str (input.GetArgumentAtIndex(cursor_index)); + completion_str.erase (cursor_char_position); + + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + CommandCompletions::eDiskFileCompletion, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + + virtual Options * + GetOptions () + { + 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; iGetImages().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. + Error 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()); + } + + 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(), + (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular) + ? "\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, + (symbol_fspec.GetFileType() != FileSpec::eFileTypeRegular) + ? "\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; + } + + virtual bool + DoExecute (Args& args, + CommandReturnObject &result) + { + 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 (frame_module_sp->GetPlatformFileSpec().Exists()) + { + 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.GetFileSpec().Exists(); + } + } + + 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.GetData()); + } + } + 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 (file_option_set) + { + result.AppendError ("specify either one or more paths to symbol files or use the --file 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 + { + PlatformSP platform_sp (target->GetPlatform()); + + for (size_t i=0; iResolveSymbolFile(*target, module_spec, symfile_spec).Success()) + module_spec.GetSymbolFileSpec() = symfile_spec; + } + + ArchSpec arch; + bool symfile_exists = module_spec.GetSymbolFileSpec().Exists(); + + if (symfile_exists) + { + if (!AddModuleSymbols (target, module_spec, flush, result)) + break; + } + else + { + char resolved_symfile_path[PATH_MAX]; + if (module_spec.GetSymbolFileSpec().GetPath (resolved_symfile_path, sizeof(resolved_symfile_path))) + { + if (strcmp (resolved_symfile_path, symfile_path) != 0) + { + result.AppendErrorWithFormat ("invalid module path '%s' with resolved path '%s'\n", symfile_path, resolved_symfile_path); + break; + } + } + result.AppendErrorWithFormat ("invalid module path '%s'\n", symfile_path); + 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", + "A set of commands for adding and managing debug symbol files.", + "target symbols ...") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetSymbolsAdd (interpreter))); + + } + virtual + ~CommandObjectTargetSymbols() + { + } + +private: + //------------------------------------------------------------------ + // For CommandObjectTargetModules only + //------------------------------------------------------------------ + DISALLOW_COPY_AND_ASSIGN (CommandObjectTargetSymbols); +}; + + +#pragma mark CommandObjectTargetStopHookAdd + +//------------------------------------------------------------------------- +// CommandObjectTargetStopHookAdd +//------------------------------------------------------------------------- + +class CommandObjectTargetStopHookAdd : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + 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 () {} + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'c': + m_class_name = option_arg; + m_sym_ctx_specified = true; + break; + + case 'e': + m_line_end = Args::StringToUInt32 (option_arg, UINT_MAX, 0, &success); + if (!success) + { + error.SetErrorStringWithFormat ("invalid end line number: \"%s\"", option_arg); + break; + } + m_sym_ctx_specified = true; + break; + + case 'l': + m_line_start = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + { + error.SetErrorStringWithFormat ("invalid start line number: \"%s\"", option_arg); + 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' : + { + m_thread_id = Args::StringToUInt64(option_arg, LLDB_INVALID_THREAD_ID, 0); + if (m_thread_id == LLDB_INVALID_THREAD_ID) + error.SetErrorStringWithFormat ("invalid thread id string '%s'", option_arg); + 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': + { + m_thread_index = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_thread_id == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid thread index string '%s'", option_arg); + m_thread_specified = true; + } + break; + case 'o': + m_use_one_liner = true; + m_one_liner = option_arg; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option %c.", short_option); + break; + } + return error; + } + + void + OptionParsingStarting () + { + 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(); + } + + + static OptionDefinition g_option_table[]; + + 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::string m_one_liner; + }; + + Options * + GetOptions () + { + return &m_options; + } + + CommandObjectTargetStopHookAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "target stop-hook add ", + "Add a hook to be executed when the target stops.", + "target stop-hook add"), + m_options (interpreter) + { + } + + ~CommandObjectTargetStopHookAdd () + { + } + + static size_t + ReadCommandsCallbackFunction (void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + Target::StopHook *new_stop_hook = ((Target::StopHook *) baton); + static bool got_interrupted; + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + out_stream->Printf ("%s\n", "Enter your stop hook command(s). Type 'DONE' to end."); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + got_interrupted = false; + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + got_interrupted = false; + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len && baton) + { + StringList *commands = new_stop_hook->GetCommandPointer(); + if (commands) + { + commands->AppendString (bytes, bytes_len); + } + } + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderInterrupt: + { + // Finish, and cancel the stop hook. + new_stop_hook->GetTarget()->RemoveStopHookByID(new_stop_hook->GetID()); + if (!batch_mode) + { + out_stream->Printf ("Stop hook cancelled.\n"); + out_stream->Flush(); + } + + reader.SetIsDone (true); + } + got_interrupted = true; + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + if (!got_interrupted && !batch_mode) + { + out_stream->Printf ("Stop hook #%" PRIu64 " added.\n", new_stop_hook->GetID()); + out_stream->Flush(); + } + break; + } + + return bytes_len; + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target) + { + Target::StopHookSP new_hook_sp; + target->AddStopHook (new_hook_sp); + + // First step, make the specifier. + std::unique_ptr specifier_ap; + if (m_options.m_sym_ctx_specified) + { + specifier_ap.reset(new SymbolContextSpecifier(m_interpreter.GetDebugger().GetSelectedTarget())); + + if (!m_options.m_module_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_module_name.c_str(), SymbolContextSpecifier::eModuleSpecified); + } + + if (!m_options.m_class_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_class_name.c_str(), SymbolContextSpecifier::eClassOrNamespaceSpecified); + } + + if (!m_options.m_file_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_file_name.c_str(), SymbolContextSpecifier::eFileSpecified); + } + + if (m_options.m_line_start != 0) + { + specifier_ap->AddLineSpecification (m_options.m_line_start, SymbolContextSpecifier::eLineStartSpecified); + } + + if (m_options.m_line_end != UINT_MAX) + { + specifier_ap->AddLineSpecification (m_options.m_line_end, SymbolContextSpecifier::eLineEndSpecified); + } + + if (!m_options.m_function_name.empty()) + { + specifier_ap->AddSpecification (m_options.m_function_name.c_str(), SymbolContextSpecifier::eFunctionSpecified); + } + } + + if (specifier_ap.get()) + new_hook_sp->SetSpecifier (specifier_ap.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); + + } + if (m_options.m_use_one_liner) + { + // Use one-liner. + new_hook_sp->GetCommandPointer()->AppendString (m_options.m_one_liner.c_str()); + result.AppendMessageWithFormat("Stop hook #%" PRIu64 " added.\n", new_hook_sp->GetID()); + } + else + { + // Otherwise gather up the command list, we'll push an input reader and suck the data from that directly into + // the new stop hook's command string. + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + if (!reader_sp) + { + result.AppendError("out of memory\n"); + result.SetStatus (eReturnStatusFailed); + target->RemoveStopHookByID (new_hook_sp->GetID()); + return false; + } + + Error err (reader_sp->Initialize (CommandObjectTargetStopHookAdd::ReadCommandsCallbackFunction, + new_hook_sp.get(), // baton + eInputReaderGranularityLine, // token size, to pass to callback function + "DONE", // end token + "> ", // prompt + true)); // echo input + if (!err.Success()) + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + target->RemoveStopHookByID (new_hook_sp->GetID()); + return false; + } + m_interpreter.GetDebugger().PushInputReader (reader_sp); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError ("invalid target\n"); + result.SetStatus (eReturnStatusFailed); + } + + return result.Succeeded(); + } +private: + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectTargetStopHookAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeOneLiner, + "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." }, + { LLDB_OPT_SET_ALL, false, "shlib", 's', required_argument, NULL, CommandCompletions::eModuleCompletion, eArgTypeShlibName, + "Set the module within which the stop-hook is to be run."}, + { LLDB_OPT_SET_ALL, false, "thread-index", 'x', required_argument, NULL, 0, eArgTypeThreadIndex, + "The stop hook is run only for the thread whose index matches this argument."}, + { LLDB_OPT_SET_ALL, false, "thread-id", 't', required_argument, NULL, 0, eArgTypeThreadID, + "The stop hook is run only for the thread whose TID matches this argument."}, + { LLDB_OPT_SET_ALL, false, "thread-name", 'T', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, CommandCompletions::eSourceFileCompletion, eArgTypeFilename, + "Specify the source file within which the stop-hook is to be run." }, + { LLDB_OPT_SET_1, false, "start-line", 'l', required_argument, NULL, 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', required_argument, NULL, 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', required_argument, NULL, 0, eArgTypeClassName, + "Specify the class within which the stop-hook is to be run." }, + { LLDB_OPT_SET_3, false, "name", 'n', required_argument, NULL, CommandCompletions::eSymbolCompletion, eArgTypeFunctionName, + "Set the function name within which the stop hook will be run." }, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#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 []") + { + } + + ~CommandObjectTargetStopHookDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + 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 = Args::StringToUInt32 (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 () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + 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 = Args::StringToUInt32 (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 []") + { + } + + ~CommandObjectTargetStopHookList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + 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", + "A set of commands for operating on debugger target stop-hooks.", + "target stop-hook []") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTargetStopHookAdd (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTargetStopHookDelete (interpreter))); + LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, + false, + "target stop-hook disable []", + "Disable a stop-hook.", + "target stop-hook disable"))); + LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTargetStopHookEnableDisable (interpreter, + true, + "target stop-hook enable []", + "Enable a stop-hook.", + "target stop-hook enable"))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTargetStopHookList (interpreter))); + } + + ~CommandObjectMultiwordTargetStopHooks() + { + } +}; + + + +#pragma mark CommandObjectMultiwordTarget + +//------------------------------------------------------------------------- +// CommandObjectMultiwordTarget +//------------------------------------------------------------------------- + +CommandObjectMultiwordTarget::CommandObjectMultiwordTarget (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "target", + "A set of commands for operating on debugger targets.", + "target []") +{ + + 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 () +{ +} + + diff --git a/source/Commands/CommandObjectTarget.h b/source/Commands/CommandObjectTarget.h new file mode 100644 index 00000000000..7b6637812c4 --- /dev/null +++ b/source/Commands/CommandObjectTarget.h @@ -0,0 +1,41 @@ +//===-- CommandObjectTarget.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectTarget_h_ +#define liblldb_CommandObjectTarget_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordTarget +//------------------------------------------------------------------------- + +class CommandObjectMultiwordTarget : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordTarget (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordTarget (); + + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectTarget_h_ diff --git a/source/Commands/CommandObjectThread.cpp b/source/Commands/CommandObjectThread.cpp new file mode 100644 index 00000000000..b8657b4361d --- /dev/null +++ b/source/Commands/CommandObjectThread.cpp @@ -0,0 +1,1526 @@ +//===-- CommandObjectThread.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectThread.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/State.h" +#include "lldb/Core/SourceManager.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/LineTable.h" +#include "lldb/Symbol/LineEntry.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/ThreadPlanStepInstruction.h" +#include "lldb/Target/ThreadPlanStepOut.h" +#include "lldb/Target/ThreadPlanStepRange.h" +#include "lldb/Target/ThreadPlanStepInRange.h" + + +using namespace lldb; +using namespace lldb_private; + + +//------------------------------------------------------------------------- +// CommandObjectThreadBacktrace +//------------------------------------------------------------------------- + +class CommandObjectThreadBacktrace : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + { + bool success; + int32_t input_count = Args::StringToSInt32 (option_arg, -1, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid integer value for option '%c'", short_option); + if (input_count < -1) + m_count = UINT32_MAX; + else + m_count = input_count; + } + break; + case 's': + { + bool success; + m_start = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid integer value for option '%c'", short_option); + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_count = UINT32_MAX; + m_start = 0; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + uint32_t m_count; + uint32_t m_start; + }; + + CommandObjectThreadBacktrace (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread backtrace", + "Show the stack for one or more threads. If no threads are specified, show the currently selected thread. Use the thread-index \"all\" to see all threads.", + NULL, + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options(interpreter) + { + 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 = eArgRepeatStar; + + // 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); + } + + ~CommandObjectThreadBacktrace() + { + } + + virtual Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + Stream &strm = result.GetOutputStream(); + + // Don't show source context when doing backtraces. + const uint32_t num_frames_with_source = 0; + if (command.GetArgumentCount() == 0) + { + Thread *thread = m_exe_ctx.GetThreadPtr(); + // Thread::GetStatus() returns the number of frames shown. + if (thread->GetStatus (strm, + m_options.m_start, + m_options.m_count, + num_frames_with_source)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + } + else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0) + { + Process *process = m_exe_ctx.GetProcessPtr(); + Mutex::Locker locker (process->GetThreadList().GetMutex()); + uint32_t num_threads = process->GetThreadList().GetSize(); + for (uint32_t i = 0; i < num_threads; i++) + { + ThreadSP thread_sp = process->GetThreadList().GetThreadAtIndex(i); + if (!thread_sp->GetStatus (strm, + m_options.m_start, + m_options.m_count, + num_frames_with_source)) + { + result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", i); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (i < num_threads - 1) + result.AppendMessage(""); + + } + } + else + { + const size_t num_args = command.GetArgumentCount(); + Process *process = m_exe_ctx.GetProcessPtr(); + Mutex::Locker locker (process->GetThreadList().GetMutex()); + std::vector thread_sps; + + for (size_t i = 0; i < num_args; i++) + { + bool success; + + uint32_t thread_idx = Args::StringToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success); + if (!success) + { + result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx)); + + if (!thread_sps[i]) + { + result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + } + + for (uint32_t i = 0; i < num_args; i++) + { + if (!thread_sps[i]->GetStatus (strm, + m_options.m_start, + m_options.m_count, + num_frames_with_source)) + { + result.AppendErrorWithFormat ("error displaying backtrace for thread: \"%s\"\n", command.GetArgumentAtIndex(i)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (i < num_args - 1) + result.AppendMessage(""); + } + } + return result.Succeeded(); + } + + CommandOptions m_options; +}; + +OptionDefinition +CommandObjectThreadBacktrace::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "count", 'c', required_argument, NULL, 0, eArgTypeCount, "How many frames to display (-1 for all)"}, +{ LLDB_OPT_SET_1, false, "start", 's', required_argument, NULL, 0, eArgTypeFrameIndex, "Frame in which to start the backtrace"}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +enum StepScope +{ + eStepScopeSource, + eStepScopeInstruction +}; + +class CommandObjectThreadStepWithTypeAndScope : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'a': + { + bool success; + m_avoid_no_debug = Args::StringToBoolean (option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid boolean value for option '%c'", short_option); + } + break; + + case 'm': + { + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + m_run_mode = (lldb::RunMode) Args::StringToOptionEnum(option_arg, enum_values, eOnlyDuringStepping, error); + } + 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 () + { + m_avoid_no_debug = true; + m_run_mode = eOnlyDuringStepping; + m_avoid_regexp.clear(); + m_step_in_target.clear(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + bool m_avoid_no_debug; + RunMode m_run_mode; + std::string m_avoid_regexp; + std::string m_step_in_target; + }; + + CommandObjectThreadStepWithTypeAndScope (CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + StepType step_type, + StepScope step_scope) : + CommandObjectParsed (interpreter, name, help, syntax, + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_step_type (step_type), + m_step_scope (step_scope), + m_options (interpreter) + { + 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); + } + + virtual + ~CommandObjectThreadStepWithTypeAndScope () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + bool synchronous_execution = m_interpreter.GetSynchronous(); + + const uint32_t num_threads = process->GetThreadList().GetSize(); + Thread *thread = NULL; + + if (command.GetArgumentCount() == 0) + { + thread = process->GetThreadList().GetSelectedThread().get(); + if (thread == NULL) + { + 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 = Args::StringToUInt32 (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 == NULL) + { + 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; + } + } + + 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) + { + if (m_step_type == eStepTypeOut) + bool_stop_other_threads = false; + else + bool_stop_other_threads = true; + } + else + bool_stop_other_threads = true; + + ThreadPlanSP new_plan_sp; + + if (m_step_type == eStepTypeInto) + { + StackFrame *frame = thread->GetStackFrameAtIndex(0).get(); + + if (frame->HasDebugInformation ()) + { + new_plan_sp = thread->QueueThreadPlanForStepInRange (abort_other_plans, + frame->GetSymbolContext(eSymbolContextEverything).line_entry.range, + frame->GetSymbolContext(eSymbolContextEverything), + m_options.m_step_in_target.c_str(), + stop_other_threads, + m_options.m_avoid_no_debug); + if (new_plan_sp && !m_options.m_avoid_regexp.empty()) + { + ThreadPlanStepInRange *step_in_range_plan = static_cast (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); + + } + 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.range, + frame->GetSymbolContext(eSymbolContextEverything), + stop_other_threads); + else + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (true, + abort_other_plans, + bool_stop_other_threads); + + } + else if (m_step_type == eStepTypeTrace) + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (false, abort_other_plans, bool_stop_other_threads); + } + else if (m_step_type == eStepTypeTraceOver) + { + new_plan_sp = thread->QueueThreadPlanForStepSingleInstruction (true, abort_other_plans, bool_stop_other_threads); + } + else if (m_step_type == eStepTypeOut) + { + new_plan_sp = thread->QueueThreadPlanForStepOut (abort_other_plans, + NULL, + false, + bool_stop_other_threads, + eVoteYes, + eVoteNoOpinion, + thread->GetSelectedFrameIndex()); + } + 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); + + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); + process->Resume (); + + + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + //EventSP event_sp; + //StateType state = process->WaitForStateChangedEvents (NULL, event_sp); + //while (! StateIsStoppedState (state)) + // { + // state = process->WaitForStateChangedEvents (NULL, event_sp); + // } + process->GetThreadList().SetSelectedThreadByID (thread->GetID()); + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.SetStatus (eReturnStatusSuccessContinuingNoResult); + } + } + else + { + result.AppendError ("Couldn't find thread plan to implement step type."); + result.SetStatus (eReturnStatusFailed); + } + return result.Succeeded(); + } + +protected: + StepType m_step_type; + StepScope m_step_scope; + CommandOptions m_options; +}; + +static 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"}, +{ 0, NULL, NULL } +}; + +static OptionEnumValueElement +g_duo_running_mode[] = +{ +{ eOnlyThisThread, "this-thread", "Run only this thread"}, +{ eAllThreads, "all-threads", "Run all threads"}, +{ 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectThreadStepWithTypeAndScope::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "avoid-no-debug", 'a', required_argument, NULL, 0, eArgTypeBoolean, "A boolean value that sets whether step-in will step over functions with no debug information."}, +{ LLDB_OPT_SET_1, false, "run-mode", 'm', required_argument, g_tri_running_mode, 0, eArgTypeRunMode, "Determine how to run other threads while stepping the current thread."}, +{ LLDB_OPT_SET_1, false, "step-over-regexp",'r', required_argument, NULL, 0, eArgTypeRegularExpression, "A regular expression that defines function names to not to stop at when stepping in."}, +{ LLDB_OPT_SET_1, false, "step-in-target", 't', required_argument, NULL, 0, eArgTypeFunctionName, "The name of the directly called function step in should stop at when stepping into."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadContinue +//------------------------------------------------------------------------- + +class CommandObjectThreadContinue : public CommandObjectParsed +{ +public: + + CommandObjectThreadContinue (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread continue", + "Continue execution of one or more threads in an active process.", + NULL, + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused) + { + 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); + } + + + virtual + ~CommandObjectThreadContinue () + { + } + + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + bool synchronous_execution = m_interpreter.GetSynchronous (); + + if (!m_interpreter.GetDebugger().GetSelectedTarget().get()) + { + 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 == NULL) + { + result.AppendError ("no process exists. Cannot continue"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + StateType state = process->GetState(); + if ((state == eStateCrashed) || (state == eStateStopped) || (state == eStateSuspended)) + { + Mutex::Locker locker (process->GetThreadList().GetMutex()); + const uint32_t num_threads = process->GetThreadList().GetSize(); + const size_t argc = command.GetArgumentCount(); + if (argc > 0) + { + std::vector resume_threads; + for (uint32_t i=0; iGetThreadList().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; + } + } + else + { + result.AppendErrorWithFormat ("invalid thread index argument: \"%s\".\n", command.GetArgumentAtIndex(i)); + 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; idxGetThreadList().GetThreadAtIndex(idx).get(); + std::vector::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.size() > 0) + result.AppendMessageWithFormat ("%u, ", thread->GetIndexID()); + else + result.AppendMessageWithFormat ("%u ", thread->GetIndexID()); + + thread->SetResumeState (eStateRunning); + } + else + { + thread->SetResumeState (eStateSuspended); + } + } + result.AppendMessageWithFormat ("in process %" PRIu64 "\n", process->GetID()); + } + } + else + { + Thread *current_thread = process->GetThreadList().GetSelectedThread().get(); + if (current_thread == NULL) + { + 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; idxGetThreadList().GetThreadAtIndex(idx).get(); + if (thread == current_thread) + { + result.AppendMessageWithFormat ("Resuming thread 0x%4.4" PRIx64 " in process %" PRIu64 "\n", thread->GetID(), process->GetID()); + thread->SetResumeState (eStateRunning); + } + else + { + thread->SetResumeState (eStateSuspended); + } + } + } + + Error error (process->Resume()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); + if (synchronous_execution) + { + state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + 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 +//------------------------------------------------------------------------- + +class CommandObjectThreadUntil : public CommandObjectParsed +{ +public: + + class CommandOptions : public Options + { + public: + uint32_t m_thread_idx; + uint32_t m_frame_idx; + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + 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 (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 't': + { + m_thread_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_INDEX32); + if (m_thread_idx == LLDB_INVALID_INDEX32) + { + error.SetErrorStringWithFormat ("invalid thread index '%s'", option_arg); + } + } + break; + case 'f': + { + m_frame_idx = Args::StringToUInt32 (option_arg, LLDB_INVALID_FRAME_ID); + if (m_frame_idx == LLDB_INVALID_FRAME_ID) + { + error.SetErrorStringWithFormat ("invalid frame index '%s'", option_arg); + } + } + break; + case 'm': + { + OptionEnumValueElement *enum_values = g_option_table[option_idx].enum_values; + lldb::RunMode run_mode = (lldb::RunMode) Args::StringToOptionEnum(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 () + { + m_thread_idx = LLDB_INVALID_THREAD_ID; + m_frame_idx = 0; + m_stop_others = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + uint32_t m_step_thread_idx; + bool m_stop_others; + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + }; + + CommandObjectThreadUntil (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread until", + "Run the current or specified thread until it reaches a given line number or leaves the current function.", + NULL, + eFlagRequiresThread | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + 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); + } + + + virtual + ~CommandObjectThreadUntil () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + bool synchronous_execution = m_interpreter.GetSynchronous (); + + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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 == NULL) + { + result.AppendError ("need a valid process to step"); + result.SetStatus (eReturnStatusFailed); + + } + else + { + Thread *thread = NULL; + uint32_t line_number; + + if (command.GetArgumentCount() != 1) + { + result.AppendErrorWithFormat ("No line number provided:\n%s", GetSyntax()); + result.SetStatus (eReturnStatusFailed); + return false; + } + + line_number = Args::StringToUInt32 (command.GetArgumentAtIndex(0), UINT32_MAX); + if (line_number == UINT32_MAX) + { + result.AppendErrorWithFormat ("invalid line number: '%s'.\n", command.GetArgumentAtIndex(0)); + result.SetStatus (eReturnStatusFailed); + return false; + } + + if (m_options.m_thread_idx == LLDB_INVALID_THREAD_ID) + { + thread = process->GetThreadList().GetSelectedThread().get(); + } + else + { + thread = process->GetThreadList().FindThreadByIndexID(m_options.m_thread_idx).get(); + } + + if (thread == NULL) + { + 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 == NULL) + { + + 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; + + 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 = NULL; + if (sc.comp_unit) + line_table = sc.comp_unit->GetLineTable(); + + if (line_table == NULL) + { + 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 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()); + line_table->FindLineEntryByAddress (fun_end_addr, function_start, &end_ptr); + + bool all_in_function = true; + + while (index_ptr <= end_ptr) + { + LineEntry line_entry; + const bool exact = false; + index_ptr = sc.comp_unit->FindLineEntry(index_ptr, line_number, sc.comp_unit, exact, &line_entry); + if (index_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; + } + index_ptr++; + } + + if (address_list.size() == 0) + { + 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); + // 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.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); + Error error (process->Resume ()); + if (error.Success()) + { + result.AppendMessageWithFormat ("Process %" PRIu64 " resuming\n", process->GetID()); + if (synchronous_execution) + { + StateType state = process->WaitForProcessToStop (NULL); + + result.SetDidChangeProcessState (true); + result.AppendMessageWithFormat ("Process %" PRIu64 " %s\n", process->GetID(), StateAsCString (state)); + 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; + +}; + +OptionDefinition +CommandObjectThreadUntil::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "frame", 'f', required_argument, NULL, 0, eArgTypeFrameIndex, "Frame index for until operation - defaults to 0"}, +{ LLDB_OPT_SET_1, false, "thread", 't', required_argument, NULL, 0, eArgTypeThreadIndex, "Thread index for the thread for until operation"}, +{ LLDB_OPT_SET_1, false, "run-mode",'m', required_argument, g_duo_running_mode, 0, eArgTypeRunMode,"Determine how to run other threads while stepping this one"}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectThreadSelect +//------------------------------------------------------------------------- + +class CommandObjectThreadSelect : public CommandObjectParsed +{ +public: + + CommandObjectThreadSelect (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "thread select", + "Select a thread as the currently active thread.", + NULL, + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + 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); + } + + + virtual + ~CommandObjectThreadSelect () + { + } + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Process *process = m_exe_ctx.GetProcessPtr(); + if (process == NULL) + { + 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 = Args::StringToUInt32(command.GetArgumentAtIndex(0), 0, 0); + + Thread *new_thread = process->GetThreadList().FindThreadByIndexID(index_id).get(); + if (new_thread == NULL) + { + 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 all current threads in a process.", + "thread list", + eFlagRequiresProcess | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ) + { + } + + ~CommandObjectThreadList() + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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); + return result.Succeeded(); + } +}; + +//------------------------------------------------------------------------- +// CommandObjectThreadReturn +//------------------------------------------------------------------------- + +class CommandObjectThreadReturn : public CommandObjectRaw +{ +public: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_from_expression (false) + { + // Keep default values of all options in one place: OptionParsingStarting () + OptionParsingStarting (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'x': + { + bool success; + bool tmp_value = Args::StringToBoolean (option_arg, false, &success); + if (success) + m_from_expression = tmp_value; + else + { + error.SetErrorStringWithFormat ("invalid boolean value '%s' for 'x' option", option_arg); + } + } + break; + default: + error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); + break; + + } + return error; + } + + void + OptionParsingStarting () + { + m_from_expression = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + bool m_from_expression; + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + }; + + virtual + Options * + GetOptions () + { + return &m_options; + } + + CommandObjectThreadReturn (CommandInterpreter &interpreter) : + CommandObjectRaw (interpreter, + "thread return", + "Return from the currently selected frame, short-circuiting execution of the frames below it, with an optional return value," + " or with the -x option from the innermost function evaluation.", + "thread return", + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_options (interpreter) + { + 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() + { + } + +protected: + + bool DoExecute + ( + const char *command, + CommandReturnObject &result + ) + { + // I am going to handle this by hand, because I don't want you to have to say: + // "thread return -- -5". + if (command[0] == '-' && command[1] == 'x') + { + if (command && command[2] != '\0') + result.AppendWarning("Return values ignored when returning from user called expressions"); + + Thread *thread = m_exe_ctx.GetThreadPtr(); + Error 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 && command[0] != '\0') + { + Target *target = m_exe_ctx.GetTargetPtr(); + EvaluateExpressionOptions options; + + options.SetUnwindOnError(true); + options.SetUseDynamic(eNoDynamicValues); + + ExecutionResults exe_results = eExecutionSetupError; + exe_results = target->EvaluateExpression (command, + frame_sp.get(), + return_valobj_sp, + options); + if (exe_results != eExecutionCompleted) + { + 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; + + } + } + + Error 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; + +}; +OptionDefinition +CommandObjectThreadReturn::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "from-expression", 'x', no_argument, NULL, 0, eArgTypeNone, "Return from the innermost expression evaluation."}, +{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordThread +//------------------------------------------------------------------------- + +CommandObjectMultiwordThread::CommandObjectMultiwordThread (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "thread", + "A set of commands for operating on one or more threads within a running process.", + "thread []") +{ + 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 ("select", CommandObjectSP (new CommandObjectThreadSelect (interpreter))); + LoadSubCommand ("until", CommandObjectSP (new CommandObjectThreadUntil (interpreter))); + LoadSubCommand ("step-in", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-in", + "Source level single step in specified thread (current thread, if none specified).", + NULL, + eStepTypeInto, + eStepScopeSource))); + + LoadSubCommand ("step-out", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-out", + "Finish executing the function of the currently selected frame and return to its call site in specified thread (current thread, if none specified).", + NULL, + eStepTypeOut, + eStepScopeSource))); + + LoadSubCommand ("step-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-over", + "Source level single step in specified thread (current thread, if none specified), stepping over calls.", + NULL, + eStepTypeOver, + eStepScopeSource))); + + LoadSubCommand ("step-inst", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-inst", + "Single step one instruction in specified thread (current thread, if none specified).", + NULL, + eStepTypeTrace, + eStepScopeInstruction))); + + LoadSubCommand ("step-inst-over", CommandObjectSP (new CommandObjectThreadStepWithTypeAndScope ( + interpreter, + "thread step-inst-over", + "Single step one instruction in specified thread (current thread, if none specified), stepping over calls.", + NULL, + eStepTypeTraceOver, + eStepScopeInstruction))); +} + +CommandObjectMultiwordThread::~CommandObjectMultiwordThread () +{ +} + + diff --git a/source/Commands/CommandObjectThread.h b/source/Commands/CommandObjectThread.h new file mode 100644 index 00000000000..52902ee36c7 --- /dev/null +++ b/source/Commands/CommandObjectThread.h @@ -0,0 +1,34 @@ +//===-- CommandObjectThread.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectThread_h_ +#define liblldb_CommandObjectThread_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" + +namespace lldb_private { + +class CommandObjectMultiwordThread : public CommandObjectMultiword +{ +public: + + CommandObjectMultiwordThread (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordThread (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectThread_h_ diff --git a/source/Commands/CommandObjectType.cpp b/source/Commands/CommandObjectType.cpp new file mode 100644 index 00000000000..b300f213db0 --- /dev/null +++ b/source/Commands/CommandObjectType.cpp @@ -0,0 +1,4112 @@ +//===-- CommandObjectType.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectType.h" + +// C Includes + +#include + +// C++ Includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/InputReaderEZ.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StringList.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupFormat.h" + +using namespace lldb; +using namespace lldb_private; + + +class ScriptAddOptions +{ + +public: + + TypeSummaryImpl::Flags m_flags; + + StringList m_target_types; + StringList m_user_source; + + bool m_regex; + + ConstString m_name; + + std::string m_category; + + ScriptAddOptions(const TypeSummaryImpl::Flags& flags, + bool regx, + const ConstString& name, + std::string catg) : + m_flags(flags), + m_regex(regx), + m_name(name), + m_category(catg) + { + } + + typedef std::shared_ptr SharedPointer; + +}; + +class SynthAddOptions +{ + +public: + + bool m_skip_pointers; + bool m_skip_references; + bool m_cascade; + bool m_regex; + StringList m_user_source; + 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_user_source(), + m_target_types(), + m_category(catg) + { + } + + typedef std::shared_ptr SharedPointer; + +}; + + + +class CommandObjectTypeSummaryAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg); + + void + OptionParsingStarting (); + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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; + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectPythonScript(ScriptAddOptions *options, + CommandReturnObject &result); + + bool + Execute_ScriptSummary (Args& command, CommandReturnObject &result); + + bool + Execute_StringSummary (Args& command, CommandReturnObject &result); + +public: + + enum SummaryFormatType + { + eRegularSummary, + eRegexSummary, + eNamedSummary + }; + + CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter); + + ~CommandObjectTypeSummaryAdd () + { + } + + static bool + AddSummary(ConstString type_name, + lldb::TypeSummaryImplSP entry, + SummaryFormatType type, + std::string category, + Error* error = NULL); +protected: + bool + DoExecute (Args& command, CommandReturnObject &result); + +}; + +class CommandObjectTypeSynthAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'C': + m_cascade = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); + 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 () + { + 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; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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; + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectPythonScript (SynthAddOptions *options, + CommandReturnObject &result); + bool + Execute_HandwritePython (Args& command, CommandReturnObject &result); + + bool + Execute_PythonClass (Args& command, CommandReturnObject &result); + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result); + +public: + + enum SynthFormatType + { + eRegularSynth, + eRegexSynth + }; + + CommandObjectTypeSynthAdd (CommandInterpreter &interpreter); + + ~CommandObjectTypeSynthAdd () + { + } + + static bool + AddSynth(ConstString type_name, + lldb::SyntheticChildrenSP entry, + SynthFormatType type, + std::string category_name, + Error* error); +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatAdd +//------------------------------------------------------------------------- + +class CommandObjectTypeFormatAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public OptionGroup + { + public: + + CommandOptions () : + OptionGroup() + { + } + + virtual + ~CommandOptions () + { + } + + virtual uint32_t + GetNumDefinitions (); + + virtual const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + virtual void + OptionParsingStarting (CommandInterpreter &interpreter) + { + m_cascade = true; + m_skip_pointers = false; + m_skip_references = false; + } + virtual Error + SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_value) + { + Error error; + const int short_option = g_option_table[option_idx].short_option; + bool success; + + switch (short_option) + { + case 'C': + m_cascade = Args::StringToBoolean(option_value, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_value); + break; + case 'p': + m_skip_pointers = true; + break; + case 'r': + m_skip_references = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_cascade; + bool m_skip_references; + bool m_skip_pointers; + }; + + OptionGroupOptions m_option_group; + OptionGroupFormat m_format_options; + CommandOptions m_command_options; + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +public: + CommandObjectTypeFormatAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format add", + "Add a new formatting style for a type.", + NULL), + m_option_group (interpreter), + 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( + "Some examples of using this command.\n" + "We use as reference the following snippet of code:\n" + "\n" + "typedef int Aint;\n" + "typedef float Afloat;\n" + "typedef Aint Bint;\n" + "typedef Afloat Bfloat;\n" + "\n" + "Aint ix = 5;\n" + "Bint iy = 5;\n" + "\n" + "Afloat fx = 3.14;\n" + "BFloat fy = 3.14;\n" + "\n" + "Typing:\n" + "type format add -f hex AInt\n" + "frame variable iy\n" + "will produce an hex display of iy, because no formatter is available for Bint and the one for Aint is used instead\n" + "To prevent this type\n" + "type format add -f hex -C no AInt\n" + "\n" + "A similar reasoning applies to\n" + "type format add -f hex -C no float -p\n" + "which now prints all floats and float&s as hexadecimal, but does not format float*s\n" + "and does not change the default display for Afloat and Bfloat objects.\n" + ); + + // Add the "--format" to all options groups + m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_ALL); + m_option_group.Append (&m_command_options); + m_option_group.Finalize(); + + } + + ~CommandObjectTypeFormatAdd () + { + } + +protected: + bool + DoExecute (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; + } + + const Format format = m_format_options.GetFormat(); + if (format == eFormatInvalid) + { + result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + TypeFormatImplSP entry; + + entry.reset(new TypeFormatImpl(format, + 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 + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + DataVisualization::ValueFormats::Add(typeCS, entry); + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } +}; + +OptionDefinition +CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, +}; + + +uint32_t +CommandObjectTypeFormatAdd::CommandOptions::GetNumDefinitions () +{ + return sizeof(g_option_table) / sizeof (OptionDefinition); +} + + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeFormatDelete : public CommandObjectParsed +{ +public: + CommandObjectTypeFormatDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format delete", + "Delete an existing formatting style for a type.", + NULL) + { + 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); + + } + + ~CommandObjectTypeFormatDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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 (DataVisualization::ValueFormats::Delete(typeCS)) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom format for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatClear +//------------------------------------------------------------------------- + +class CommandObjectTypeFormatClear : public CommandObjectParsed +{ +public: + CommandObjectTypeFormatClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format clear", + "Delete all existing format styles.", + NULL) + { + } + + ~CommandObjectTypeFormatClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + DataVisualization::ValueFormats::Clear(); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatList +//------------------------------------------------------------------------- + +bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, ConstString type, const lldb::TypeFormatImplSP& entry); + +class CommandObjectTypeFormatList; + +struct CommandObjectTypeFormatList_LoopCallbackParam { + CommandObjectTypeFormatList* self; + CommandReturnObject* result; + RegularExpression* regex; + CommandObjectTypeFormatList_LoopCallbackParam(CommandObjectTypeFormatList* S, CommandReturnObject* R, + RegularExpression* X = NULL) : self(S), result(R), regex(X) {} +}; + +class CommandObjectTypeFormatList : public CommandObjectParsed +{ +public: + CommandObjectTypeFormatList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type format list", + "Show a list of current formatting styles.", + NULL) + { + 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); + } + + ~CommandObjectTypeFormatList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeFormatList_LoopCallbackParam *param; + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result,regex); + } + else + param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result); + DataVisualization::ValueFormats::LoopThrough(CommandObjectTypeFormatList_LoopCallback, param); + delete param; + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + bool + LoopCallback (ConstString type, + const lldb::TypeFormatImplSP& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type.AsCString())) + { + result->GetOutputStream().Printf ("%s: %s\n", type.AsCString(), + entry->GetDescription().c_str()); + } + return true; + } + + friend bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, ConstString type, const lldb::TypeFormatImplSP& entry); + +}; + +bool +CommandObjectTypeFormatList_LoopCallback ( + void* pt2self, + ConstString type, + const lldb::TypeFormatImplSP& entry) +{ + CommandObjectTypeFormatList_LoopCallbackParam* param = (CommandObjectTypeFormatList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type, entry, param->regex, param->result); +} + + +#ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryAdd +//------------------------------------------------------------------------- + +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\"\"\""; + +class TypeScriptAddInputReader : public InputReaderEZ +{ +private: + DISALLOW_COPY_AND_ASSIGN (TypeScriptAddInputReader); +public: + TypeScriptAddInputReader(Debugger& debugger) : + InputReaderEZ(debugger) + {} + + virtual + ~TypeScriptAddInputReader() + { + } + + virtual void ActivateHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_summary_addreader_instructions); + if (data.reader.GetPrompt()) + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + + virtual void ReactivateHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void GotTokenHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (data.bytes && data.bytes_len && data.baton) + { + ((ScriptAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); + } + if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void InterruptHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = data.reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + data.reader.SetIsDone (true); + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + virtual void EOFHandler(HandlerData& data) + { + data.reader.SetIsDone (true); + } + virtual void DoneHandler(HandlerData& data) + { + StreamSP out_stream = data.reader.GetDebugger().GetAsyncOutputStream(); + ScriptAddOptions *options_ptr = ((ScriptAddOptions*)data.baton); + if (!options_ptr) + { + out_stream->Printf ("internal synchronization information missing or invalid.\n"); + out_stream->Flush(); + return; + } + + ScriptAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (!interpreter) + { + out_stream->Printf ("no script interpreter.\n"); + out_stream->Flush(); + return; + } + std::string funct_name_str; + if (!interpreter->GenerateTypeScriptFunction (options->m_user_source, + funct_name_str)) + { + out_stream->Printf ("unable to generate a function.\n"); + out_stream->Flush(); + return; + } + if (funct_name_str.empty()) + { + out_stream->Printf ("unable to obtain a valid function name from the script interpreter.\n"); + out_stream->Flush(); + return; + } + // now I have a valid function name, let's add this as script for every type in the list + + TypeSummaryImplSP script_format; + script_format.reset(new ScriptSummaryFormat(options->m_flags, + funct_name_str.c_str(), + options->m_user_source.CopyList(" ").c_str())); + + Error 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()) + { + out_stream->Printf ("%s", error.AsCString()); + out_stream->Flush(); + return; + } + } + + 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()) + { + out_stream->Printf ("%s", error.AsCString()); + out_stream->Flush(); + return; + } + } + else + { + out_stream->Printf ("%s", error.AsCString()); + out_stream->Flush(); + return; + } + } + else + { + if (error.AsCString()) + { + out_stream->PutCString (error.AsCString()); + out_stream->Flush(); + } + return; + } + } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +Error +CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg) +{ + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'C': + m_flags.SetCascades(Args::StringToBoolean(option_arg, true, &success)); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); + break; + case 'e': + m_flags.SetDontShowChildren(false); + 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.SetCString(option_arg); + break; + case 'o': + m_python_script = std::string(option_arg); + m_is_add_script = true; + break; + case 'F': + m_python_function = std::string(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 () +{ + 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 +void +CommandObjectTypeSummaryAdd::CollectPythonScript (ScriptAddOptions *options, + CommandReturnObject &result) +{ + InputReaderSP reader_sp (new TypeScriptAddInputReader(m_interpreter.GetDebugger())); + if (reader_sp && options) + { + + InputReaderEZ::InitializationParameters ipr; + + Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +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.reset(new ScriptSummaryFormat(m_options.m_flags, + funct_name, + code.c_str())); + + ScriptInterpreter *interpreter = m_interpreter.GetScriptInterpreter(); + + if (interpreter && interpreter->CheckObjectExists(funct_name) == false) + 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 = m_interpreter.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.reset(new ScriptSummaryFormat(m_options.m_flags, + funct_name_str.c_str(), + code.c_str())); + } + else // use an InputReader 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 (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + if (typeA && *typeA) + options->m_target_types << typeA; + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + CollectPythonScript(options,result); + 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 + + Error error; + + for (size_t i = 0; i < command.GetArgumentCount(); i++) + { + const char *type_name = command.GetArgumentAtIndex(i); + CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), + 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 + + +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; + } + + Error error; + + lldb::TypeSummaryImplSP entry(new StringSummaryFormat(m_options.m_flags, + format_cstr)); + + if (error.Fail()) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + // now I have a valid format, let's add it to every type + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + if (!typeA || typeA[0] == '\0') + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + ConstString typeCS(typeA); + + 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.", + NULL), + 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( + "Some examples of using this command.\n" + "We use as reference the following snippet of code:\n" + "struct JustADemo\n" + "{\n" + "int* ptr;\n" + "float value;\n" + "JustADemo(int p = 1, float v = 0.1) : ptr(new int(p)), value(v) {}\n" + "};\n" + "JustADemo object(42,3.14);\n" + "struct AnotherDemo : public JustADemo\n" + "{\n" + "uint8_t byte;\n" + "AnotherDemo(uint8_t b = 'E', int p = 1, float v = 0.1) : JustADemo(p,v), byte(b) {}\n" + "};\n" + "AnotherDemo *another_object = new AnotherDemo('E',42,3.14);\n" + "\n" + "type summary add --summary-string \"the answer is ${*var.ptr}\" JustADemo\n" + "when typing frame variable object you will get \"the answer is 42\"\n" + "type summary add --summary-string \"the answer is ${*var.ptr}, and the question is ${var.value}\" JustADemo\n" + "when typing frame variable object you will get \"the answer is 42 and the question is 3.14\"\n" + "\n" + "Alternatively, you could also say\n" + "type summary add --summary-string \"${var%V} -> ${*var}\" \"int *\"\n" + "and replace the above summary string with\n" + "type summary add --summary-string \"the answer is ${var.ptr}, and the question is ${var.value}\" JustADemo\n" + "to obtain a similar result\n" + "\n" + "To add a summary valid for both JustADemo and AnotherDemo you can use the scoping operator, as in:\n" + "type summary add --summary-string \"${var.ptr}, ${var.value},{${var.byte}}\" JustADemo -C yes\n" + "\n" + "This will be used for both variables of type JustADemo and AnotherDemo. To prevent this, change the -C to read -C no\n" + "If you do not want pointers to be shown using that summary, you can use the -p option, as in:\n" + "type summary add --summary-string \"${var.ptr}, ${var.value},{${var.byte}}\" JustADemo -C yes -p\n" + "A similar option -r exists for references.\n" + "\n" + "If you simply want a one-line summary of the content of your variable, without typing an explicit string to that effect\n" + "you can use the -c option, without giving any summary string:\n" + "type summary add -c JustADemo\n" + "frame variable object\n" + "the output being similar to (ptr=0xsomeaddress, value=3.14)\n" + "\n" + "If you want to display some summary text, but also expand the structure of your object, you can add the -e option, as in:\n" + "type summary add -e --summary-string \"*ptr = ${*var.ptr}\" JustADemo\n" + "Here the value of the int* is displayed, followed by the standard LLDB sequence of children objects, one per line.\n" + "to get an output like:\n" + "\n" + "*ptr = 42 {\n" + " ptr = 0xsomeaddress\n" + " value = 3.14\n" + "}\n" + "\n" + "You can also add Python summaries, in which case you will use lldb public API to gather information from your variables" + "and elaborate them to a meaningful summary inside a script written in Python. The variable object will be passed to your" + "script as an SBValue object. The following example might help you when starting to use the Python summaries feature:\n" + "type summary add JustADemo -o \"value = valobj.GetChildMemberWithName('value'); return 'My value is ' + value.GetValue();\"\n" + "If you prefer to type your scripts on multiple lines, you will use the -P option and then type your script, ending it with " + "the word DONE on a line by itself to mark you're finished editing your code:\n" + "(lldb)type summary add JustADemo -P\n" + " value = valobj.GetChildMemberWithName('value');\n" + " return 'My value is ' + value.GetValue();\n" + "DONE\n" + "(lldb) <-- type further LLDB commands here\n" + ); +} + +bool +CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &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 + } + + return Execute_StringSummary(command, result); +} + +bool +CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, + TypeSummaryImplSP entry, + SummaryFormatType type, + std::string category_name, + Error* error) +{ + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + + if (type == eRegularSummary) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + 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()); + type = eRegexSummary; + } + } + + if (type == eRegexSummary) + { + RegularExpressionSP typeRX(new RegularExpression()); + if (!typeRX->Compile(type_name.GetCString())) + { + if (error) + error->SetErrorString("regex format error (maybe this is not really a regex?)"); + return false; + } + + category->GetRegexSummaryNavigator()->Delete(type_name); + category->GetRegexSummaryNavigator()->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->GetSummaryNavigator()->Add(type_name, entry); + return true; + } +} + +OptionDefinition +CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "no-value", 'v', no_argument, NULL, 0, eArgTypeNone, "Don't show the value, just show the summary, for this type."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { LLDB_OPT_SET_1 , true, "inline-children", 'c', no_argument, NULL, 0, eArgTypeNone, "If true, inline all child values into summary string."}, + { LLDB_OPT_SET_1 , false, "omit-names", 'O', no_argument, NULL, 0, eArgTypeNone, "If true, omit value names in the summary display."}, + { LLDB_OPT_SET_2 , true, "summary-string", 's', required_argument, NULL, 0, eArgTypeSummaryString, "Summary string used to display text and object contents."}, + { LLDB_OPT_SET_3, false, "python-script", 'o', required_argument, NULL, 0, eArgTypePythonScript, "Give a one-liner Python script as part of the command."}, + { LLDB_OPT_SET_3, false, "python-function", 'F', required_argument, NULL, 0, eArgTypePythonFunction, "Give the name of a Python function to use for this type."}, + { LLDB_OPT_SET_3, false, "input-python", 'P', no_argument, NULL, 0, eArgTypeNone, "Input Python code to use for this type manually."}, + { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "expand", 'e', no_argument, NULL, 0, eArgTypeNone, "Expand aggregate data types to show children on separate lines."}, + { LLDB_OPT_SET_2 | LLDB_OPT_SET_3, false, "name", 'n', required_argument, NULL, 0, eArgTypeName, "A name for this summary string."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeSummaryDelete : public CommandObjectParsed +{ +private: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + m_category = "default"; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + std::string m_category; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& category_sp) + { + ConstString *name = (ConstString*)param; + category_sp->Delete(*name, eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); + return true; + } + +public: + CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary delete", + "Delete an existing summary style for a type.", + NULL), + m_options(interpreter) + { + 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); + + } + + ~CommandObjectTypeSummaryDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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::LoopThrough(PerCategoryCallback, &typeCS); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + bool delete_category = category->Delete(typeCS, + eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); + bool delete_named = DataVisualization::NamedSummaryFormats::Delete(typeCS); + + if (delete_category || delete_named) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom summary for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } +}; + +OptionDefinition +CommandObjectTypeSummaryDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Delete from every category."}, + { LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectTypeSummaryClear : public CommandObjectParsed +{ +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + m_delete_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + bool m_delete_named; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + cate->GetSummaryNavigator()->Clear(); + cate->GetRegexSummaryNavigator()->Clear(); + return true; + + } + +public: + CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary clear", + "Delete all existing summary styles.", + NULL), + m_options(interpreter) + { + } + + ~CommandObjectTypeSummaryClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_options.m_delete_all) + DataVisualization::Categories::LoopThrough(PerCategoryCallback, NULL); + + 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(NULL), category); + category->Clear(eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); + } + + DataVisualization::NamedSummaryFormats::Clear(); + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeSummaryClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Clear every category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryList +//------------------------------------------------------------------------- + +bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, ConstString type, const StringSummaryFormat::SharedPointer& entry); +bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const StringSummaryFormat::SharedPointer& entry); + +class CommandObjectTypeSummaryList; + +struct CommandObjectTypeSummaryList_LoopCallbackParam { + CommandObjectTypeSummaryList* self; + CommandReturnObject* result; + RegularExpression* regex; + RegularExpression* cate_regex; + CommandObjectTypeSummaryList_LoopCallbackParam(CommandObjectTypeSummaryList* S, CommandReturnObject* R, + RegularExpression* X = NULL, + RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {} +}; + +class CommandObjectTypeSummaryList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'w': + m_category_regex = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_category_regex = ""; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_category_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeSummaryList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type summary list", + "Show a list of current summary styles.", + NULL), + m_options(interpreter) + { + 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); + } + + ~CommandObjectTypeSummaryList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeSummaryList_LoopCallbackParam *param; + RegularExpression* cate_regex = + m_options.m_category_regex.empty() ? NULL : + new RegularExpression(m_options.m_category_regex.c_str()); + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex,cate_regex); + } + else + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,NULL,cate_regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + + if (DataVisualization::NamedSummaryFormats::GetCount() > 0) + { + result.GetOutputStream().Printf("Named summaries:\n"); + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex); + } + else + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result); + DataVisualization::NamedSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); + delete param; + } + + if (cate_regex) + delete cate_regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + + CommandObjectTypeSummaryList_LoopCallbackParam* param = + (CommandObjectTypeSummaryList_LoopCallbackParam*)param_vp; + CommandReturnObject* result = param->result; + + const char* cate_name = cate->GetName(); + + // if the category is disabled or empty and there is no regex, just skip it + if ((cate->IsEnabled() == false || cate->GetCount(eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary) == 0) && param->cate_regex == NULL) + return true; + + // if we have a regex and this category does not match it, just skip it + if(param->cate_regex != NULL && strcmp(cate_name,param->cate_regex->GetText()) != 0 && param->cate_regex->Execute(cate_name) == false) + return true; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->GetSummaryNavigator()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param_vp); + + if (cate->GetRegexSummaryNavigator()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); + cate->GetRegexSummaryNavigator()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param_vp); + } + return true; + } + + + bool + LoopCallback (const char* type, + const lldb::TypeSummaryImplSP& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || strcmp(type,regex->GetText()) == 0 || regex->Execute(type)) + result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str()); + return true; + } + + friend bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, ConstString type, const lldb::TypeSummaryImplSP& entry); + friend bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const lldb::TypeSummaryImplSP& entry); +}; + +bool +CommandObjectTypeSummaryList_LoopCallback ( + void* pt2self, + ConstString type, + const lldb::TypeSummaryImplSP& entry) +{ + CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type.AsCString(), entry, param->regex, param->result); +} + +bool +CommandObjectTypeRXSummaryList_LoopCallback ( + void* pt2self, + lldb::RegularExpressionSP regex, + const lldb::TypeSummaryImplSP& entry) +{ + CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} + +OptionDefinition +CommandObjectTypeSummaryList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryEnable +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryEnable : public CommandObjectParsed +{ +public: + CommandObjectTypeCategoryEnable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category enable", + "Enable a category as a source of formatters.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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; + } + + if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) + { + // we want to make sure to enable "system" last and "default" first + DataVisualization::Categories::Enable(ConstString("default"), TypeCategoryMap::First); + uint32_t num_categories = DataVisualization::Categories::GetCount(); + for (uint32_t i = 0; i < num_categories; i++) + { + lldb::TypeCategoryImplSP category_sp = DataVisualization::Categories::GetCategoryAtIndex(i); + if (category_sp) + { + if ( ::strcmp(category_sp->GetName(), "system") == 0 || + ::strcmp(category_sp->GetName(), "default") == 0 ) + continue; + else + DataVisualization::Categories::Enable(category_sp, TypeCategoryMap::Default); + } + } + DataVisualization::Categories::Enable(ConstString("system"), TypeCategoryMap::Last); + } + else + { + 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.get()) + { + if (cate->GetCount() == 0) + { + result.AppendWarning("empty category enabled (typo?)"); + } + } + } + } + + 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.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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 +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryDisable : public CommandObjectParsed +{ +public: + CommandObjectTypeCategoryDisable (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category disable", + "Disable a category as a source of formatters.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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; + } + + if (argc == 1 && strcmp(command.GetArgumentAtIndex(0),"*") == 0) + { + uint32_t num_categories = DataVisualization::Categories::GetCount(); + for (uint32_t i = 0; i < num_categories; i++) + { + lldb::TypeCategoryImplSP category_sp = DataVisualization::Categories::GetCategoryAtIndex(i); + // no need to check if the category is enabled - disabling a disabled category has no effect + if (category_sp) + DataVisualization::Categories::Disable(category_sp); + } + } + else + { + // 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); + } + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryList +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryList : public CommandObjectParsed +{ +private: + + struct CommandObjectTypeCategoryList_CallbackParam + { + CommandReturnObject* result; + RegularExpression* regex; + + CommandObjectTypeCategoryList_CallbackParam(CommandReturnObject* res, + RegularExpression* rex = NULL) : + result(res), + regex(rex) + { + } + + }; + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + CommandObjectTypeCategoryList_CallbackParam* param = + (CommandObjectTypeCategoryList_CallbackParam*)param_vp; + CommandReturnObject* result = param->result; + RegularExpression* regex = param->regex; + + const char* cate_name = cate->GetName(); + + if (regex == NULL || strcmp(cate_name, regex->GetText()) == 0 || regex->Execute(cate_name)) + result->GetOutputStream().Printf("Category %s is%s enabled\n", + cate_name, + (cate->IsEnabled() ? "" : " not")); + return true; + } +public: + CommandObjectTypeCategoryList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type category list", + "Provide a list of all existing categories.", + NULL) + { + 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 () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + RegularExpression* regex = NULL; + + if (argc == 0) + ; + else if (argc == 1) + regex = new RegularExpression(command.GetArgumentAtIndex(0)); + else + { + result.AppendErrorWithFormat ("%s takes 0 or one arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + CommandObjectTypeCategoryList_CallbackParam param(&result, + regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback, ¶m); + + if (regex) + delete regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFilterList +//------------------------------------------------------------------------- + +bool CommandObjectTypeFilterList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); +bool CommandObjectTypeFilterRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); + +class CommandObjectTypeFilterList; + +struct CommandObjectTypeFilterList_LoopCallbackParam { + CommandObjectTypeFilterList* self; + CommandReturnObject* result; + RegularExpression* regex; + RegularExpression* cate_regex; + CommandObjectTypeFilterList_LoopCallbackParam(CommandObjectTypeFilterList* S, CommandReturnObject* R, + RegularExpression* X = NULL, + RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {} +}; + +class CommandObjectTypeFilterList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'w': + m_category_regex = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_category_regex = ""; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_category_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeFilterList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter list", + "Show a list of current filters.", + NULL), + m_options(interpreter) + { + 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); + } + + ~CommandObjectTypeFilterList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeFilterList_LoopCallbackParam *param; + RegularExpression* cate_regex = + m_options.m_category_regex.empty() ? NULL : + new RegularExpression(m_options.m_category_regex.c_str()); + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeFilterList_LoopCallbackParam(this,&result,regex,cate_regex); + } + else + param = new CommandObjectTypeFilterList_LoopCallbackParam(this,&result,NULL,cate_regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + + if (cate_regex) + delete cate_regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + + const char* cate_name = cate->GetName(); + + CommandObjectTypeFilterList_LoopCallbackParam* param = + (CommandObjectTypeFilterList_LoopCallbackParam*)param_vp; + CommandReturnObject* result = param->result; + + // if the category is disabled or empty and there is no regex, just skip it + if ((cate->IsEnabled() == false || cate->GetCount(eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter) == 0) && param->cate_regex == NULL) + return true; + + // if we have a regex and this category does not match it, just skip it + if(param->cate_regex != NULL && strcmp(cate_name,param->cate_regex->GetText()) != 0 && param->cate_regex->Execute(cate_name) == false) + return true; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->GetFilterNavigator()->LoopThrough(CommandObjectTypeFilterList_LoopCallback, param_vp); + + if (cate->GetRegexFilterNavigator()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based filters (slower):\n"); + cate->GetRegexFilterNavigator()->LoopThrough(CommandObjectTypeFilterRXList_LoopCallback, param_vp); + } + + return true; + } + + bool + LoopCallback (const char* type, + const SyntheticChildren::SharedPointer& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type)) + result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str()); + return true; + } + + friend bool CommandObjectTypeFilterList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); + friend bool CommandObjectTypeFilterRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); +}; + +bool +CommandObjectTypeFilterList_LoopCallback (void* pt2self, + ConstString type, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeFilterList_LoopCallbackParam* param = (CommandObjectTypeFilterList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type.AsCString(), entry, param->regex, param->result); +} + +bool +CommandObjectTypeFilterRXList_LoopCallback (void* pt2self, + lldb::RegularExpressionSP regex, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeFilterList_LoopCallbackParam* param = (CommandObjectTypeFilterList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} + + +OptionDefinition +CommandObjectTypeFilterList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeSynthList +//------------------------------------------------------------------------- + +bool CommandObjectTypeSynthList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); +bool CommandObjectTypeSynthRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); + +class CommandObjectTypeSynthList; + +struct CommandObjectTypeSynthList_LoopCallbackParam { + CommandObjectTypeSynthList* self; + CommandReturnObject* result; + RegularExpression* regex; + RegularExpression* cate_regex; + CommandObjectTypeSynthList_LoopCallbackParam(CommandObjectTypeSynthList* S, CommandReturnObject* R, + RegularExpression* X = NULL, + RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {} +}; + +class CommandObjectTypeSynthList : public CommandObjectParsed +{ + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'w': + m_category_regex = std::string(option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_category_regex = ""; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_category_regex; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeSynthList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic list", + "Show a list of current synthetic providers.", + NULL), + m_options(interpreter) + { + 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); + } + + ~CommandObjectTypeSynthList () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeSynthList_LoopCallbackParam *param; + RegularExpression* cate_regex = + m_options.m_category_regex.empty() ? NULL : + new RegularExpression(m_options.m_category_regex.c_str()); + + if (argc == 1) + { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,regex,cate_regex); + } + else + param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,NULL,cate_regex); + + DataVisualization::Categories::LoopThrough(PerCategoryCallback,param); + + if (cate_regex) + delete cate_regex; + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + static bool + PerCategoryCallback(void* param_vp, + const lldb::TypeCategoryImplSP& cate) + { + + CommandObjectTypeSynthList_LoopCallbackParam* param = + (CommandObjectTypeSynthList_LoopCallbackParam*)param_vp; + CommandReturnObject* result = param->result; + + const char* cate_name = cate->GetName(); + + // if the category is disabled or empty and there is no regex, just skip it + if ((cate->IsEnabled() == false || cate->GetCount(eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth) == 0) && param->cate_regex == NULL) + return true; + + // if we have a regex and this category does not match it, just skip it + if(param->cate_regex != NULL && strcmp(cate_name,param->cate_regex->GetText()) != 0 && param->cate_regex->Execute(cate_name) == false) + return true; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->GetSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp); + + if (cate->GetRegexSyntheticNavigator()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based synthetic providers (slower):\n"); + cate->GetRegexSyntheticNavigator()->LoopThrough(CommandObjectTypeSynthRXList_LoopCallback, param_vp); + } + + return true; + } + + bool + LoopCallback (const char* type, + const SyntheticChildren::SharedPointer& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type)) + result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str()); + return true; + } + + friend bool CommandObjectTypeSynthList_LoopCallback(void* pt2self, ConstString type, const SyntheticChildren::SharedPointer& entry); + friend bool CommandObjectTypeSynthRXList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SyntheticChildren::SharedPointer& entry); +}; + +bool +CommandObjectTypeSynthList_LoopCallback (void* pt2self, + ConstString type, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeSynthList_LoopCallbackParam* param = (CommandObjectTypeSynthList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type.AsCString(), entry, param->regex, param->result); +} + +bool +CommandObjectTypeSynthRXList_LoopCallback (void* pt2self, + lldb::RegularExpressionSP regex, + const SyntheticChildren::SharedPointer& entry) +{ + CommandObjectTypeSynthList_LoopCallbackParam* param = (CommandObjectTypeSynthList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} + + +OptionDefinition +CommandObjectTypeSynthList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON +//------------------------------------------------------------------------- +// CommandObjectTypeFilterDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeFilterDelete : public CommandObjectParsed +{ +private: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + m_category = "default"; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + std::string m_category; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + ConstString *name = (ConstString*)param; + return cate->Delete(*name, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter); + } + +public: + CommandObjectTypeFilterDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter delete", + "Delete an existing filter for a type.", + NULL), + m_options(interpreter) + { + 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); + + } + + ~CommandObjectTypeFilterDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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::LoopThrough(PerCategoryCallback, (void*)&typeCS); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + bool delete_category = category->GetFilterNavigator()->Delete(typeCS); + delete_category = category->GetRegexFilterNavigator()->Delete(typeCS) || delete_category; + + if (delete_category) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom synthetic provider for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } +}; + +OptionDefinition +CommandObjectTypeFilterDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Delete from every category."}, + { LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeSynthDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeSynthDelete : public CommandObjectParsed +{ +private: + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_delete_all = false; + m_category = "default"; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + std::string m_category; + + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + ConstString* name = (ConstString*)param; + return cate->Delete(*name, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth); + } + +public: + CommandObjectTypeSynthDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic delete", + "Delete an existing synthetic provider for a type.", + NULL), + m_options(interpreter) + { + 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); + + } + + ~CommandObjectTypeSynthDelete () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + 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::LoopThrough(PerCategoryCallback, (void*)&typeCS); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(m_options.m_category.c_str()), category); + + bool delete_category = category->GetSyntheticNavigator()->Delete(typeCS); + delete_category = category->GetRegexSyntheticNavigator()->Delete(typeCS) || delete_category; + + if (delete_category) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom synthetic provider for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } +}; + +OptionDefinition +CommandObjectTypeSynthDelete::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Delete from every category."}, + { LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +//------------------------------------------------------------------------- +// CommandObjectTypeFilterClear +//------------------------------------------------------------------------- + +class CommandObjectTypeFilterClear : public CommandObjectParsed +{ +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + m_delete_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + bool m_delete_named; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + cate->Clear(eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter); + return true; + + } + +public: + CommandObjectTypeFilterClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter clear", + "Delete all existing filters.", + NULL), + m_options(interpreter) + { + } + + ~CommandObjectTypeFilterClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_options.m_delete_all) + DataVisualization::Categories::LoopThrough(PerCategoryCallback, NULL); + + 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(NULL), category); + category->GetFilterNavigator()->Clear(); + category->GetRegexFilterNavigator()->Clear(); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeFilterClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Clear every category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#ifndef LLDB_DISABLE_PYTHON +//------------------------------------------------------------------------- +// CommandObjectTypeSynthClear +//------------------------------------------------------------------------- + +class CommandObjectTypeSynthClear : public CommandObjectParsed +{ +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + m_delete_all = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_delete_all; + bool m_delete_named; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + + static bool + PerCategoryCallback(void* param, + const lldb::TypeCategoryImplSP& cate) + { + cate->Clear(eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth); + return true; + + } + +public: + CommandObjectTypeSynthClear (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type synthetic clear", + "Delete all existing synthetic providers.", + NULL), + m_options(interpreter) + { + } + + ~CommandObjectTypeSynthClear () + { + } + +protected: + bool + DoExecute (Args& command, CommandReturnObject &result) + { + + if (m_options.m_delete_all) + DataVisualization::Categories::LoopThrough(PerCategoryCallback, NULL); + + 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(NULL), category); + category->GetSyntheticNavigator()->Clear(); + category->GetRegexSyntheticNavigator()->Clear(); + } + + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeSynthClear::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeNone, "Clear every category."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// TypeSynthAddInputReader +//------------------------------------------------------------------------- + +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" + "Optionally, you can also define a method:\n" + " def update(self):\n" + "if your synthetic provider is holding on to any per-object state variables (currently, this is not implemented because of the way LLDB handles instances of SBValue and you should not rely on object persistence and per-object state)\n" + "class synthProvider:"; + +class TypeSynthAddInputReader : public InputReaderEZ +{ +public: + TypeSynthAddInputReader(Debugger& debugger) : + InputReaderEZ(debugger) + {} + + virtual + ~TypeSynthAddInputReader() + { + } + + virtual void ActivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_synth_addreader_instructions); + if (data.reader.GetPrompt()) + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + + virtual void ReactivateHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void GotTokenHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + if (data.bytes && data.bytes_len && data.baton) + { + ((SynthAddOptions*)data.baton)->m_user_source.AppendString(data.bytes, data.bytes_len); + } + if (!data.reader.IsDone() && data.reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", data.reader.GetPrompt()); + out_stream->Flush(); + } + } + virtual void InterruptHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + bool batch_mode = data.GetBatchMode(); + data.reader.SetIsDone (true); + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + virtual void EOFHandler(HandlerData& data) + { + data.reader.SetIsDone (true); + } + virtual void DoneHandler(HandlerData& data) + { + StreamSP out_stream = data.GetOutStream(); + SynthAddOptions *options_ptr = ((SynthAddOptions*)data.baton); + if (!options_ptr) + { + out_stream->Printf ("internal synchronization data missing.\n"); + out_stream->Flush(); + return; + } + + SynthAddOptions::SharedPointer options(options_ptr); // this will ensure that we get rid of the pointer when going out of scope + + ScriptInterpreter *interpreter = data.reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (!interpreter) + { + out_stream->Printf ("no script interpreter.\n"); + out_stream->Flush(); + return; + } + std::string class_name_str; + if (!interpreter->GenerateTypeSynthClass (options->m_user_source, + class_name_str)) + { + out_stream->Printf ("unable to generate a class.\n"); + out_stream->Flush(); + return; + } + if (class_name_str.empty()) + { + out_stream->Printf ("unable to obtain a proper name for the class.\n"); + out_stream->Flush(); + return; + } + + // everything should be fine now, let's add the synth provider class + + SyntheticChildrenSP synth_provider; + synth_provider.reset(new 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); + + Error error; + + for (size_t i = 0; i < options->m_target_types.GetSize(); i++) + { + const char *type_name = options->m_target_types.GetStringAtIndex(i); + ConstString typeCS(type_name); + if (typeCS) + { + if (!CommandObjectTypeSynthAdd::AddSynth(typeCS, + synth_provider, + options->m_regex ? CommandObjectTypeSynthAdd::eRegexSynth : CommandObjectTypeSynthAdd::eRegularSynth, + options->m_category, + &error)) + { + out_stream->Printf("%s\n", error.AsCString()); + out_stream->Flush(); + return; + } + } + else + { + out_stream->Printf ("invalid type name.\n"); + out_stream->Flush(); + return; + } + } + } + +private: + DISALLOW_COPY_AND_ASSIGN (TypeSynthAddInputReader); +}; + +void +CommandObjectTypeSynthAdd::CollectPythonScript (SynthAddOptions *options, + CommandReturnObject &result) +{ + InputReaderSP reader_sp (new TypeSynthAddInputReader(m_interpreter.GetDebugger())); + if (reader_sp && options) + { + + InputReaderEZ::InitializationParameters ipr; + + Error err (reader_sp->Initialize (ipr.SetBaton(options).SetPrompt(" "))); + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +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); + + const size_t argc = command.GetArgumentCount(); + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + if (typeA && *typeA) + options->m_target_types << typeA; + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + CollectPythonScript(options,result); + 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 = m_interpreter.GetScriptInterpreter(); + + if (interpreter && interpreter->CheckObjectExists(impl->GetPythonClassName()) == false) + 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); + + Error error; + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + { + if (!AddSynth(typeCS, + entry, + m_options.m_regex ? eRegexSynth : eRegularSynth, + m_options.m_category, + &error)) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError("empty typenames not allowed"); + 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.", + NULL), + 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); + +} + +bool +CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, + SyntheticChildrenSP entry, + SynthFormatType type, + std::string category_name, + Error* error) +{ + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + + if (type == eRegularSynth) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + 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()); + type = eRegularSynth; + } + } + + 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.GetCString())) + { + if (error) + error->SetErrorString("regex format error (maybe this is not really a regex?)"); + return false; + } + + category->GetRegexSyntheticNavigator()->Delete(type_name); + category->GetRegexSyntheticNavigator()->Add(typeRX, entry); + + return true; + } + else + { + category->GetSyntheticNavigator()->Add(type_name, entry); + return true; + } +} + +bool +CommandObjectTypeSynthAdd::DoExecute (Args& command, CommandReturnObject &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; + } +} + +OptionDefinition +CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_2, false, "python-class", 'l', required_argument, NULL, 0, eArgTypePythonClass, "Use this Python class to produce synthetic children."}, + { LLDB_OPT_SET_3, false, "input-python", 'P', no_argument, NULL, 0, eArgTypeNone, "Type Python code to generate a class that provides synthetic children."}, + { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +class CommandObjectTypeFilterAdd : public CommandObjectParsed +{ + +private: + + class CommandOptions : public Options + { + typedef std::vector option_vector; + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'C': + m_cascade = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for cascade: %s", option_arg); + 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 () + { + 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; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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; + + virtual Options * + GetOptions () + { + return &m_options; + } + + enum FilterFormatType + { + eRegularFilter, + eRegexFilter + }; + + bool + AddFilter(ConstString type_name, + SyntheticChildrenSP entry, + FilterFormatType type, + std::string category_name, + Error* error) + { + lldb::TypeCategoryImplSP category; + DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + + if (type == eRegularFilter) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + 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()); + 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.GetCString())) + { + if (error) + error->SetErrorString("regex format error (maybe this is not really a regex?)"); + return false; + } + + category->GetRegexFilterNavigator()->Delete(type_name); + category->GetRegexFilterNavigator()->Add(typeRX, entry); + + return true; + } + else + { + category->GetFilterNavigator()->Add(type_name, entry); + return true; + } + } + + +public: + + CommandObjectTypeFilterAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "type filter add", + "Add a new filter for a type.", + NULL), + 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( + "Some examples of using this command.\n" + "We use as reference the following snippet of code:\n" + "\n" + "class Foo {;\n" + " int a;\n" + " int b;\n" + " int c;\n" + " int d;\n" + " int e;\n" + " int f;\n" + " int g;\n" + " int h;\n" + " int i;\n" + "} \n" + "Typing:\n" + "type filter add --child a --child g Foo\n" + "frame variable a_foo\n" + "will produce an output where only a and g are displayed\n" + "Other children of a_foo (b,c,d,e,f,h and i) are available by asking for them, as in:\n" + "frame variable a_foo.b a_foo.c ... a_foo.i\n" + "\n" + "Use option --raw to frame variable prevails on the filter\n" + "frame variable a_foo --raw\n" + "shows all the children of a_foo (a thru i) as if no filter was defined\n" + ); + } + + ~CommandObjectTypeFilterAdd () + { + } + +protected: + bool + DoExecute (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_expr_paths.size() == 0) + { + result.AppendErrorWithFormat ("%s needs one or more children.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + SyntheticChildrenSP entry; + + TypeFilterImpl* impl = new TypeFilterImpl(SyntheticChildren::Flags().SetCascades(m_options.m_cascade). + SetSkipPointers(m_options.m_skip_pointers). + SetSkipReferences(m_options.m_skip_references)); + + entry.reset(impl); + + // 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++) + impl->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); + + Error error; + + for (size_t i = 0; i < argc; i++) + { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + { + if (!AddFilter(typeCS, + entry, + m_options.m_regex ? eRegexFilter : eRegularFilter, + m_options.m_category, + &error)) + { + result.AppendError(error.AsCString()); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + +}; + +OptionDefinition +CommandObjectTypeFilterAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."}, + { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, + { LLDB_OPT_SET_ALL, false, "child", 'c', required_argument, NULL, 0, eArgTypeExpressionPath, "Include this expression path in the synthetic view."}, + { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +class CommandObjectTypeFormat : public CommandObjectMultiword +{ +public: + CommandObjectTypeFormat (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type format", + "A set of commands for editing variable value display options", + "type format [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFormatAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter))); + } + + + ~CommandObjectTypeFormat () + { + } +}; + +#ifndef LLDB_DISABLE_PYTHON + +class CommandObjectTypeSynth : public CommandObjectMultiword +{ +public: + CommandObjectTypeSynth (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type synthetic", + "A set of commands for operating on synthetic type representations", + "type synthetic [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSynthAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSynthClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSynthDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSynthList (interpreter))); + } + + + ~CommandObjectTypeSynth () + { + } +}; + +#endif // #ifndef LLDB_DISABLE_PYTHON + +class CommandObjectTypeFilter : public CommandObjectMultiword +{ +public: + CommandObjectTypeFilter (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type filter", + "A set of commands for operating on type filters", + "type synthetic [] ") + { + 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 () + { + } +}; + +class CommandObjectTypeCategory : public CommandObjectMultiword +{ +public: + CommandObjectTypeCategory (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type category", + "A set of commands for operating on categories", + "type category [] ") + { + 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 () + { + } +}; + +class CommandObjectTypeSummary : public CommandObjectMultiword +{ +public: + CommandObjectTypeSummary (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type summary", + "A set of commands for editing variable summary display options", + "type summary [] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSummaryAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter))); + } + + + ~CommandObjectTypeSummary () + { + } +}; + +//------------------------------------------------------------------------- +// CommandObjectType +//------------------------------------------------------------------------- + +CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type", + "A set of commands for operating on the type system", + "type []") +{ + 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 +} + + +CommandObjectType::~CommandObjectType () +{ +} + + diff --git a/source/Commands/CommandObjectType.h b/source/Commands/CommandObjectType.h new file mode 100644 index 00000000000..c796902cf3b --- /dev/null +++ b/source/Commands/CommandObjectType.h @@ -0,0 +1,37 @@ +//===-- CommandObjectType.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectType_h_ +#define liblldb_CommandObjectType_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" + +namespace lldb_private { + +class CommandObjectType : public CommandObjectMultiword +{ +public: + CommandObjectType (CommandInterpreter &interpreter); + + virtual + ~CommandObjectType (); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectType_h_ diff --git a/source/Commands/CommandObjectVersion.cpp b/source/Commands/CommandObjectVersion.cpp new file mode 100644 index 00000000000..2d950a89c9b --- /dev/null +++ b/source/Commands/CommandObjectVersion.cpp @@ -0,0 +1,53 @@ +//===-- CommandObjectVersion.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectVersion.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectVersion +//------------------------------------------------------------------------- + +CommandObjectVersion::CommandObjectVersion (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, "version", "Show version of LLDB debugger.", "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/source/Commands/CommandObjectVersion.h b/source/Commands/CommandObjectVersion.h new file mode 100644 index 00000000000..1fdbed60c65 --- /dev/null +++ b/source/Commands/CommandObjectVersion.h @@ -0,0 +1,43 @@ +//===-- CommandObjectVersion.h ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectVersion_h_ +#define liblldb_CommandObjectVersion_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectVersion +//------------------------------------------------------------------------- + +class CommandObjectVersion : public CommandObjectParsed +{ +public: + + CommandObjectVersion (CommandInterpreter &interpreter); + + virtual + ~CommandObjectVersion (); + +protected: + virtual bool + DoExecute (Args& args, + CommandReturnObject &result); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectVersion_h_ diff --git a/source/Commands/CommandObjectWatchpoint.cpp b/source/Commands/CommandObjectWatchpoint.cpp new file mode 100644 index 00000000000..6f70f39c1c7 --- /dev/null +++ b/source/Commands/CommandObjectWatchpoint.cpp @@ -0,0 +1,1394 @@ +//===-- CommandObjectWatchpoint.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectWatchpoint.h" +#include "CommandObjectWatchpointCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/WatchpointList.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/Target.h" + +#include + +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 == NULL) + { + 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; +} + +// FIXME: This doesn't seem to be the right place for this functionality. +#include "llvm/ADT/StringRef.h" +static inline void StripLeadingSpaces(llvm::StringRef &Str) +{ + while (!Str.empty() && isspace(Str[0])) + Str = Str.substr(1); +} + +// 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 &wp_ids) +{ + // Pre-condition: args.GetArgumentCount() > 0. + if (args.GetArgumentCount() == 0) + { + if (target == NULL) + 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 StrRefArgs; + std::pair Pair; + size_t i; + int32_t idx; + // Go through the argments and make a canonical form of arg list containing + // only numbers with possible "-" in between. + for (i = 0; i < args.GetArgumentCount(); ++i) { + llvm::StringRef Arg(args.GetArgumentAtIndex(i)); + if ((idx = WithRSAIndex(Arg)) == -1) { + StrRefArgs.push_back(Arg); + continue; + } + // The Arg contains the range specifier, split it, then. + Pair = Arg.split(RSA[idx]); + if (!Pair.first.empty()) + StrRefArgs.push_back(Pair.first); + StrRefArgs.push_back(Minus); + if (!Pair.second.empty()) + StrRefArgs.push_back(Pair.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. + if (in_range) + return false; + + return true; // Success! +} + +//------------------------------------------------------------------------- +// CommandObjectWatchpointList +//------------------------------------------------------------------------- +#pragma mark List + +class CommandObjectWatchpointList : public CommandObjectParsed +{ +public: + CommandObjectWatchpointList (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "watchpoint list", + "List all watchpoints at configurable levels of detail.", + NULL), + m_options(interpreter) + { + 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); + } + + virtual + ~CommandObjectWatchpointList () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options(interpreter), + m_level(lldb::eDescriptionLevelBrief) // Watchpoint List defaults to brief descriptions + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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 () + { + m_level = lldb::eDescriptionLevelFull; + } + + const OptionDefinition * + GetDefinitions () + { + return g_option_table; + } + + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + lldb::DescriptionLevel m_level; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (target == NULL) + { + 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; + Error 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(); + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + 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 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; +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointList::Options +//------------------------------------------------------------------------- +#pragma mark List::CommandOptions +OptionDefinition +CommandObjectWatchpointList::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "brief", 'b', no_argument, NULL, 0, eArgTypeNone, + "Give a brief description of the watchpoint (no location info)."}, + + { LLDB_OPT_SET_2, false, "full", 'f', no_argument, NULL, 0, eArgTypeNone, + "Give a full description of the watchpoint and its locations."}, + + { LLDB_OPT_SET_3, false, "verbose", 'v', no_argument, NULL, 0, eArgTypeNone, + "Explain everything we know about the watchpoint (for debugging debugger bugs)." }, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// 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.", + NULL) + { + 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); + } + + virtual + ~CommandObjectWatchpointEnable () {} + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + 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. (%lu watchpoints)\n", num_watchpoints); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; enable them. + std::vector 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(); + } + +private: +}; + +//------------------------------------------------------------------------- +// 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.", + NULL) + { + 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); + } + + + virtual + ~CommandObjectWatchpointDisable () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + 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. (%lu watchpoints)\n", num_watchpoints); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError("Disable all watchpoints failed\n"); + result.SetStatus(eReturnStatusFailed); + } + } + else + { + // Particular watchpoints selected; disable them. + std::vector 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.", + NULL) + { + 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); + } + + virtual + ~CommandObjectWatchpointDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + 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. (%lu watchpoints)\n", num_watchpoints); + } + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; delete them. + std::vector 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 +//------------------------------------------------------------------------- + +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.", + NULL), + m_options (interpreter) + { + 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); + } + + virtual + ~CommandObjectWatchpointIgnore () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_ignore_count (0) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'i': + { + m_ignore_count = Args::StringToUInt32(option_arg, UINT32_MAX, 0); + if (m_ignore_count == UINT32_MAX) + error.SetErrorStringWithFormat ("invalid ignore count '%s'", option_arg); + } + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_ignore_count = 0; + } + + const OptionDefinition * + GetDefinitions () + { + return g_option_table; + } + + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + uint32_t m_ignore_count; + }; + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + 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. (%lu watchpoints)\n", num_watchpoints); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + // Particular watchpoints selected; ignore them. + std::vector 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; +}; + +#pragma mark Ignore::CommandOptions +OptionDefinition +CommandObjectWatchpointIgnore::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_ALL, true, "ignore-count", 'i', required_argument, NULL, 0, eArgTypeCount, "Set the number of times this watchpoint is skipped before stopping." }, + { 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectWatchpointModify +//------------------------------------------------------------------------- +#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.", + NULL), + m_options (interpreter) + { + 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); + } + + virtual + ~CommandObjectWatchpointModify () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_condition (), + m_condition_passed (false) + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + const int short_option = m_getopt_table[option_idx].val; + + switch (short_option) + { + case 'c': + if (option_arg != NULL) + m_condition.assign (option_arg); + else + m_condition.clear(); + m_condition_passed = true; + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_condition.clear(); + m_condition_passed = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + std::string m_condition; + bool m_condition_passed; + }; + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + if (!CheckTargetForWatchpointOperations(target, result)) + return false; + + Mutex::Locker locker; + target->GetWatchpointList().GetListMutex(locker); + + 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 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; +}; + +#pragma mark Modify::CommandOptions +OptionDefinition +CommandObjectWatchpointModify::CommandOptions::g_option_table[] = +{ +{ LLDB_OPT_SET_ALL, false, "condition", 'c', required_argument, NULL, 0, eArgTypeExpression, "The watchpoint stops only if this condition expression evaluates to true."}, +{ 0, false, NULL, 0 , 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// 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 '-x' option to specify the byte size to watch for. " + "If no '-w' option is specified, it defaults to write. " + "If no '-x' 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.", + NULL, + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_option_group (interpreter), + m_option_watchpoint () + { + SetHelpLong( + "Examples: \n\ + \n\ + watchpoint set variable -w read_wriate my_global_var \n\ + # Watch my_global_var for read/write access, with the region to watch corresponding to the byte size of the data type.\n"); + + 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 '-x' options into our option group. + m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + virtual + ~CommandObjectWatchpointSetVariable () {} + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + static size_t GetVariableCallback (void *baton, + const char *name, + VariableList &variable_list) + { + Target *target = static_cast(baton); + if (target) + { + return target->GetImages().FindGlobalVariables (ConstString(name), + true, + UINT32_MAX, + variable_list); + } + return 0; + } + + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.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... + Error 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; + + Error 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); + } + + ClangASTType clang_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; + } + clang_type = valobj_sp->GetClangType(); + } + else + { + const char *error_cstr = error.AsCString(NULL); + 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, &clang_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=%lu, variable expression='%s').\n", + addr, size, command.GetArgumentAtIndex(0)); + if (error.AsCString(NULL)) + 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 '-x' option to specify the byte size to watch for. " + "If no '-w' option is specified, it defaults to write. " + "If no '-x' 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.", + NULL, + eFlagRequiresFrame | + eFlagTryTargetAPILock | + eFlagProcessMustBeLaunched | + eFlagProcessMustBePaused ), + m_option_group (interpreter), + m_option_watchpoint () + { + SetHelpLong( + "Examples: \n\ + \n\ + watchpoint set expression -w write -x 1 -- foo + 32\n\ + # Watch write access for the 1-byte region pointed to by the address 'foo + 32'.\n"); + + 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 '-x' options into our option group. + m_option_group.Append (&m_option_watchpoint, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); + m_option_group.Finalize(); + } + + + virtual + ~CommandObjectWatchpointSetExpression () {} + + // Overrides base class's behavior where WantsCompletion = !WantsRawCommandString. + virtual bool + WantsCompletion() { return true; } + + virtual Options * + GetOptions () + { + return &m_option_group; + } + +protected: + virtual bool + DoExecute (const char *raw_command, CommandReturnObject &result) + { + m_option_group.NotifyOptionParsingStarting(); // This is a raw command, so notify the option group + + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + StackFrame *frame = m_exe_ctx.GetFramePtr(); + + Args command(raw_command); + const char *expr = NULL; + if (raw_command[0] == '-') + { + // We have some options and these options MUST end with --. + const char *end_options = NULL; + const char *s = raw_command; + while (s && s[0]) + { + end_options = ::strstr (s, "--"); + if (end_options) + { + end_options += 2; // Get past the "--" + if (::isspace (end_options[0])) + { + expr = end_options; + while (::isspace (*expr)) + ++expr; + break; + } + } + s = end_options; + } + + if (end_options) + { + Args args (raw_command, end_options - raw_command); + if (!ParseOptions (args, result)) + return false; + + Error error (m_option_group.NotifyOptionParsingFinished()); + if (error.Fail()) + { + result.AppendError (error.AsCString()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + } + + if (expr == NULL) + expr = raw_command; + + // 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 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) + .SetUnwindOnError(true) + .SetKeepInMemory(false) + .SetRunOthers(true) + .SetTimeoutUsec(0); + + ExecutionResults expr_result = target->EvaluateExpression (expr, + frame, + valobj_sp, + options); + if (expr_result != eExecutionCompleted) + { + result.GetErrorStream().Printf("error: expression evaluation of address to watch failed\n"); + result.GetErrorStream().Printf("expression evaluated: %s\n", expr); + 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. + ClangASTType clang_type(valobj_sp->GetClangType()); + + Error error; + Watchpoint *wp = target->CreateWatchpoint(addr, size, &clang_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=%lu).\n", + addr, size); + if (error.AsCString(NULL)) + 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", + "A set of commands for setting a watchpoint.", + "watchpoint set []") + { + + LoadSubCommand ("variable", CommandObjectSP (new CommandObjectWatchpointSetVariable (interpreter))); + LoadSubCommand ("expression", CommandObjectSP (new CommandObjectWatchpointSetExpression (interpreter))); + } + + + virtual + ~CommandObjectWatchpointSet () {} + +}; + +//------------------------------------------------------------------------- +// CommandObjectMultiwordWatchpoint +//------------------------------------------------------------------------- +#pragma mark MultiwordWatchpoint + +CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "watchpoint", + "A set of commands for operating on watchpoints.", + "watchpoint []") +{ + 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() +{ +} + diff --git a/source/Commands/CommandObjectWatchpoint.h b/source/Commands/CommandObjectWatchpoint.h new file mode 100644 index 00000000000..1b1ebd7764a --- /dev/null +++ b/source/Commands/CommandObjectWatchpoint.h @@ -0,0 +1,43 @@ +//===-- CommandObjectWatchpoint.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectWatchpoint_h_ +#define liblldb_CommandObjectWatchpoint_h_ + +// C Includes +// C++ Includes + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObjectMultiword.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/OptionGroupWatchpoint.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordWatchpoint +//------------------------------------------------------------------------- + +class CommandObjectMultiwordWatchpoint : public CommandObjectMultiword +{ +public: + CommandObjectMultiwordWatchpoint (CommandInterpreter &interpreter); + + virtual + ~CommandObjectMultiwordWatchpoint (); + + static bool + VerifyWatchpointIDs(Target *target, Args &args, std::vector &wp_ids); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectWatchpoint_h_ diff --git a/source/Commands/CommandObjectWatchpointCommand.cpp b/source/Commands/CommandObjectWatchpointCommand.cpp new file mode 100644 index 00000000000..4e200465031 --- /dev/null +++ b/source/Commands/CommandObjectWatchpointCommand.cpp @@ -0,0 +1,850 @@ +//===-- CommandObjectWatchpointCommand.cpp ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +// C++ Includes + + +#include "CommandObjectWatchpointCommand.h" +#include "CommandObjectWatchpoint.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Breakpoint/Watchpoint.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/State.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointCommandAdd +//------------------------------------------------------------------------- + + +class CommandObjectWatchpointCommandAdd : public CommandObjectParsed +{ +public: + + CommandObjectWatchpointCommandAdd (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "add", + "Add a set of commands to a watchpoint, to be executed whenever the watchpoint is hit.", + NULL), + m_options (interpreter) + { + SetHelpLong ( +"\nGeneral information about entering watchpoint commands \n\ +------------------------------------------------------ \n\ + \n\ +This command will cause you to be prompted to enter the command or set \n\ +of commands you wish to be executed when the specified watchpoint is \n\ +hit. You will be told to enter your command(s), and will see a '> ' \n\ +prompt. Because you can enter one or many commands to be executed when \n\ +a watchpoint is hit, you will continue to be prompted after each \n\ +new-line that you enter, until you enter the word 'DONE', which will \n\ +cause the commands you have entered to be stored with the watchpoint \n\ +and executed when the watchpoint is hit. \n\ + \n\ +Syntax checking is not necessarily done when watchpoint commands are \n\ +entered. An improperly written watchpoint command will attempt to get \n\ +executed when the watchpoint gets hit, and usually silently fail. If \n\ +your watchpoint command does not appear to be getting executed, go \n\ +back and check your syntax. \n\ + \n\ + \n\ +Special information about PYTHON watchpoint commands \n\ +---------------------------------------------------- \n\ + \n\ +You may enter either one line of Python or multiple lines of Python \n\ +(including defining whole functions, if desired). If you enter a \n\ +single line of Python, that will be passed to the Python interpreter \n\ +'as is' when the watchpoint gets hit. If you enter function \n\ +definitions, they will be passed to the Python interpreter as soon as \n\ +you finish entering the watchpoint command, and they can be called \n\ +later (don't forget to add calls to them, if you want them called when \n\ +the watchpoint is hit). If you enter multiple lines of Python that \n\ +are not function definitions, they will be collected into a new, \n\ +automatically generated Python function, and a call to the newly \n\ +generated function will be attached to the watchpoint. \n\ + \n\ +This auto-generated function is passed in two arguments: \n\ + \n\ + frame: an SBFrame object representing the frame which hit the watchpoint. \n\ + From the frame you can get back to the thread and process. \n\ + wp: the watchpoint that was hit. \n\ + \n\ +Important Note: Because loose Python code gets collected into functions, \n\ +if you want to access global variables in the 'loose' code, you need to \n\ +specify that they are global, using the 'global' keyword. Be sure to \n\ +use correct Python syntax, including indentation, when entering Python \n\ +watchpoint commands. \n\ + \n\ +As a third option, you can pass the name of an already existing Python function \n\ +and that function will be attached to the watchpoint. It will get passed the \n\ +frame and wp_loc arguments mentioned above. \n\ + \n\ +Example Python one-line watchpoint command: \n\ + \n\ +(lldb) watchpoint command add -s python 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> print \"Hit this watchpoint!\" \n\ +> DONE \n\ + \n\ +As a convenience, this also works for a short Python one-liner: \n\ +(lldb) watchpoint command add -s python 1 -o \"import time; print time.asctime()\" \n\ +(lldb) run \n\ +Launching '.../a.out' (x86_64) \n\ +(lldb) Fri Sep 10 12:17:45 2010 \n\ +Process 21778 Stopped \n\ +* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread \n\ + 36 \n\ + 37 int c(int val)\n\ + 38 {\n\ + 39 -> return val + 3;\n\ + 40 }\n\ + 41 \n\ + 42 int main (int argc, char const *argv[])\n\ +(lldb) \n\ + \n\ +Example multiple line Python watchpoint command, using function definition: \n\ + \n\ +(lldb) watchpoint command add -s python 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> def watchpoint_output (wp_no): \n\ +> out_string = \"Hit watchpoint number \" + repr (wp_no) \n\ +> print out_string \n\ +> return True \n\ +> watchpoint_output (1) \n\ +> DONE \n\ + \n\ + \n\ +Example multiple line Python watchpoint command, using 'loose' Python: \n\ + \n\ +(lldb) watchpoint command add -s p 1 \n\ +Enter your Python command(s). Type 'DONE' to end. \n\ +> global wp_count \n\ +> wp_count = wp_count + 1 \n\ +> print \"Hit this watchpoint \" + repr(wp_count) + \" times!\" \n\ +> DONE \n\ + \n\ +In this case, since there is a reference to a global variable, \n\ +'wp_count', you will also need to make sure 'wp_count' exists and is \n\ +initialized: \n\ + \n\ +(lldb) script \n\ +>>> wp_count = 0 \n\ +>>> quit() \n\ + \n\ +(lldb) \n\ + \n\ + \n\ +Final Note: If you get a warning that no watchpoint command was generated, \n\ +but you did not get any syntax errors, you probably forgot to add a call \n\ +to your functions. \n\ + \n\ +Special information about debugger command watchpoint commands \n\ +-------------------------------------------------------------- \n\ + \n\ +You may enter any debugger command, exactly as you would at the \n\ +debugger prompt. You may enter as many debugger commands as you like, \n\ +but do NOT enter more than one command per line. \n" ); + + 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); + } + + virtual + ~CommandObjectWatchpointCommandAdd () {} + + virtual Options * + GetOptions () + { + return &m_options; + } + + void + CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result) + { + InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); + std::unique_ptr data_ap(new WatchpointOptions::CommandData()); + if (reader_sp && data_ap.get()) + { + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); + + Error err (reader_sp->Initialize (CommandObjectWatchpointCommandAdd::GenerateWatchpointCommandCallback, + wp_options, // callback_data + eInputReaderGranularityLine, // token size, to pass to callback function + "DONE", // end token + "> ", // prompt + true)); // echo input + if (err.Success()) + { + m_interpreter.GetDebugger().PushInputReader (reader_sp); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } + + } + + /// Set a one-liner as the callback for the watchpoint. + void + SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner) + { + std::unique_ptr data_ap(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_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + data_ap->stop_on_error = m_options.m_stop_on_error; + + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp); + + return; + } + + static size_t + GenerateWatchpointCommandCallback (void *callback_data, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + switch (notification) + { + case eInputReaderActivate: + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes && bytes_len && callback_data) + { + WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; + if (wp_options) + { + Baton *wp_options_baton = wp_options->GetBaton(); + if (wp_options_baton) + ((WatchpointOptions::CommandData *)wp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len); + } + } + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush(); + } + break; + + case eInputReaderInterrupt: + { + // Finish, and cancel the watchpoint command. + reader.SetIsDone (true); + WatchpointOptions *wp_options = (WatchpointOptions *) callback_data; + if (wp_options) + { + Baton *wp_options_baton = wp_options->GetBaton (); + if (wp_options_baton) + { + ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->user_source.Clear(); + ((WatchpointOptions::CommandData *) wp_options_baton->m_data)->script_source.clear(); + } + } + if (!batch_mode) + { + out_stream->Printf ("Warning: No command attached to watchpoint.\n"); + out_stream->Flush(); + } + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone (true); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; + } + + static bool + WatchpointOptionsCallbackFunction (void *baton, + StoppointCallbackContext *context, + lldb::user_id_t watch_id) + { + bool ret_value = true; + if (baton == NULL) + 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); + + bool stop_on_continue = true; + bool echo_commands = false; + bool print_results = true; + + debugger.GetCommandInterpreter().HandleCommands (commands, + &exe_ctx, + stop_on_continue, + data->stop_on_error, + echo_commands, + print_results, + eLazyBoolNo, + result); + result.GetImmediateOutputStream()->Flush(); + result.GetImmediateErrorStream()->Flush(); + } + } + return ret_value; + } + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter), + m_use_commands (false), + m_use_script_language (false), + m_script_language (eScriptLanguageNone), + m_use_one_liner (false), + m_one_liner(), + m_function_name() + { + } + + virtual + ~CommandOptions () {} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error 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) Args::StringToOptionEnum (option_arg, + g_option_table[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 = Args::StringToBoolean(option_arg, false, &success); + if (!success) + error.SetErrorStringWithFormat("invalid value for stop-on-error: \"%s\"", option_arg); + } + 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 () + { + 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(); + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // 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: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + 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 == false && m_options.m_function_name.size()) + { + result.AppendError ("need to enable scripting to have a function run as a watchpoint command"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + std::vector 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 == NULL) continue; + + WatchpointOptions *wp_options = wp->GetOptions(); + // Skip this watchpoint if wp_options is not good. + if (wp_options == NULL) 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) + { + m_interpreter.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.size()) + { + std::string oneliner(m_options.m_function_name); + oneliner += "(frame, wp, internal_dict)"; + m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options, + oneliner.c_str()); + } + else + { + m_interpreter.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; + static const char *g_reader_instructions; + +}; + +const char * +CommandObjectWatchpointCommandAdd::g_reader_instructions = "Enter your debugger command(s). Type 'DONE' to end."; + +// 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 OptionEnumValueElement +g_script_option_enumeration[4] = +{ + { 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."}, + { 0, NULL, NULL } +}; + +OptionDefinition +CommandObjectWatchpointCommandAdd::CommandOptions::g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeOneLiner, + "Specify a one-line watchpoint command inline. Be sure to surround it with quotes." }, + + { LLDB_OPT_SET_ALL, false, "stop-on-error", 'e', required_argument, NULL, 0, eArgTypeBoolean, + "Specify whether watchpoint command execution should terminate on error." }, + + { LLDB_OPT_SET_ALL, false, "script-type", 's', required_argument, g_script_option_enumeration, 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', required_argument, NULL, 0, eArgTypePythonFunction, + "Give the name of a Python function to run as command for this watchpoint. Be sure to give a module name if appropriate."}, + + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +//------------------------------------------------------------------------- +// CommandObjectWatchpointCommandDelete +//------------------------------------------------------------------------- + +class CommandObjectWatchpointCommandDelete : public CommandObjectParsed +{ +public: + CommandObjectWatchpointCommandDelete (CommandInterpreter &interpreter) : + CommandObjectParsed (interpreter, + "delete", + "Delete the set of commands from a watchpoint.", + NULL) + { + 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); + } + + + virtual + ~CommandObjectWatchpointCommandDelete () {} + +protected: + virtual bool + DoExecute (Args& command, CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + 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 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.", + NULL) + { + 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); + } + + virtual + ~CommandObjectWatchpointCommandList () {} + +protected: + virtual bool + DoExecute (Args& command, + CommandReturnObject &result) + { + Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get(); + + if (target == NULL) + { + 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 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", + "A set of commands for adding, removing and examining bits of code to be executed when the watchpoint is hit (watchpoint 'commmands').", + "command [] ") +{ + 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 () +{ +} + + diff --git a/source/Commands/CommandObjectWatchpointCommand.h b/source/Commands/CommandObjectWatchpointCommand.h new file mode 100644 index 00000000000..c2faf7187db --- /dev/null +++ b/source/Commands/CommandObjectWatchpointCommand.h @@ -0,0 +1,46 @@ +//===-- CommandObjectWatchpointCommand.h ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectWatchpointCommand_h_ +#define liblldb_CommandObjectWatchpointCommand_h_ + +// C Includes +// C++ Includes + + +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-types.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandObjectMultiword.h" + + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectMultiwordWatchpoint +//------------------------------------------------------------------------- + +class CommandObjectWatchpointCommand : public CommandObjectMultiword +{ +public: + CommandObjectWatchpointCommand (CommandInterpreter &interpreter); + + virtual + ~CommandObjectWatchpointCommand (); + +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectWatchpointCommand_h_ diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp new file mode 100644 index 00000000000..8d599d80ad4 --- /dev/null +++ b/source/Core/Address.cpp @@ -0,0 +1,1045 @@ +//===-- Address.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Address.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Section.h" +#include "lldb/Symbol/Block.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/Target.h" +#include "lldb/Symbol/SymbolVendor.h" + +#include "llvm/ADT/Triple.h" + +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 == NULL) + return 0; + + TargetSP target_sp (exe_scope->CalculateTarget()); + if (target_sp) + { + Error 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 == NULL) + 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 == NULL || 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 == NULL) + 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 == NULL || byte_size == 0) + return 0; + std::vector 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); + + data.Dump (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 == NULL) + 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), lldb::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; + + data.Dump (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.load(); + } + 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; + } + // 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) + { + // No section, we just return the offset since it is the value in this case + return m_offset; + } + + 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; + } + } + // The section isn't resolved or no process was supplied so we can't + // return a valid file address. + return LLDB_INVALID_ADDRESS; +} + +addr_t +Address::GetCallableLoadAddress (Target *target, bool is_indirect) const +{ + if (is_indirect && target) { + ProcessSP processSP = target->GetProcessSP(); + Error error; + if (processSP.get()) + return processSP->ResolveIndirectFunction(this, error); + } + + addr_t code_addr = GetLoadAddress (target); + + 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) const +{ + addr_t code_addr = GetLoadAddress (target); + if (code_addr != LLDB_INVALID_ADDRESS) + code_addr = target->GetOpcodeLoadAddress (code_addr, GetAddressClass()); + return code_addr; +} + +bool +Address::SetOpcodeLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (SetLoadAddress (load_addr, target)) + { + if (target) + m_offset = target->GetOpcodeLoadAddress (m_offset, GetAddressClass()); + 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 NULL, 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.load()); + } + else + { + s->Address(m_offset, addr_size); + } + break; + + case DumpStyleSectionPointerOffset: + s->Printf("(Section *)%p + ", section_sp.get()); + s->Address(m_offset, addr_size); + break; + + case DumpStyleModuleWithFileAddress: + if (section_sp) + s->Printf("%s[", section_sp->GetModule()->GetFileSpec().GetFilename().AsCString()); + // Fall through + 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); + 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: + 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->GetAddress().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 || func_sc.symbol) + { + 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); + 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 || pointer_sc.symbol) + { + s->PutCString(": "); + pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false); + } + } + } + } + } + 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; + if (sc.function == NULL && sc.symbol != NULL) + { + // If we have just a symbol make sure it is in the right section + if (sc.symbol->ValueIsAddress()) + { + if (sc.symbol->GetAddress().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); + } + 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, 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->GetAddress().GetSection() != GetSection()) + sc.symbol = NULL; + } + 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_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\", type= \"%s\", location =", + var->GetID(), + var->GetName().GetCString(), + var->GetType()->GetName().GetCString()); + 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) + { + Error 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.GetData(), strm.GetSize()); + return true; + } + } + } + } + } + if (fallback_style != DumpStyleInvalid) + return Dump (s, exe_scope, fallback_style, DumpStyleInvalid, addr_size); + return false; + } + break; + } + + return true; +} + +uint32_t +Address::CalculateSymbolContext (SymbolContext *sc, uint32_t 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 NULL; +} + +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 NULL; +} + +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 NULL; +} + +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 NULL; +} + +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 != NULL); + 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. + module_sp->GetSymbolVendor(); + return obj_file->GetAddressClass (GetFileAddress()); + } + } + return eAddressClassUnknown; +} + +bool +Address::SetLoadAddress (lldb::addr_t load_addr, Target *target) +{ + if (target && target->GetSectionLoadList().ResolveLoadAddress(load_addr, *this)) + return true; + m_section_wp.reset(); + m_offset = load_addr; + return false; +} + diff --git a/source/Core/AddressRange.cpp b/source/Core/AddressRange.cpp new file mode 100644 index 00000000000..835a01d82aa --- /dev/null +++ b/source/Core/AddressRange.cpp @@ -0,0 +1,208 @@ +//===-- AddressRange.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressRange.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +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 §ion, 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; +} + +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; + // fall through + 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()); + } + 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", this, 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/source/Core/AddressResolver.cpp b/source/Core/AddressResolver.cpp new file mode 100644 index 00000000000..5369d960f25 --- /dev/null +++ b/source/Core/AddressResolver.cpp @@ -0,0 +1,67 @@ +//===-- AddressResolver.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolver.h" + + +// Project includes + +#include "lldb/Core/Address.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" +#include "lldb/lldb-private-log.h" + +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 & +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/source/Core/AddressResolverFileLine.cpp b/source/Core/AddressResolverFileLine.cpp new file mode 100644 index 00000000000..f7004c8bb08 --- /dev/null +++ b/source/Core/AddressResolverFileLine.cpp @@ -0,0 +1,102 @@ +//===-- AddressResolverFileLine.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolverFileLine.h" + +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/lldb-private-log.h" + +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(""), + m_line_number); + } + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::Depth +AddressResolverFileLine::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +void +AddressResolverFileLine::GetDescription (Stream *s) +{ + s->Printf ("File and line address - file: \"%s\" line: %u", m_file_spec.GetFilename().AsCString(), m_line_number); +} + + diff --git a/source/Core/AddressResolverName.cpp b/source/Core/AddressResolverName.cpp new file mode 100644 index 00000000000..dd22e17402b --- /dev/null +++ b/source/Core/AddressResolverName.cpp @@ -0,0 +1,255 @@ +//===-- AddressResolverName.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/AddressResolverName.h" + +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/lldb-private-log.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 (NULL), + m_regex (), + m_match_type (type) +{ + if (m_match_type == AddressResolver::Regexp) + { + if (!m_regex.Compile (m_func_name.AsCString())) + { + 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 (NULL), + m_class_name (NULL), + 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 () +{ +} + +// 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, + NULL, + 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 funcion list and the symbol list + if (func_list.GetSize()) + { + for (i = 0; i < func_list.GetSize(); i++) + { + if (func_list.GetContextAtIndex(i, sc) == false) + continue; + + if (sc.function == NULL) + 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->GetAddress()) + { + 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->GetAddress(); + 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; +} + +Searcher::Depth +AddressResolverName::GetDepth() +{ + return Searcher::eDepthModule; +} + +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()); + else + s->Printf("'%s'", m_func_name.AsCString()); +} + diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp new file mode 100644 index 00000000000..27d62c358bb --- /dev/null +++ b/source/Core/ArchSpec.cpp @@ -0,0 +1,893 @@ +//===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ArchSpec.h" + +#include +#include + +#include + +#include "llvm/Support/ELF.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + +#define ARCH_SPEC_SEPARATOR_CHAR '-' + + +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 *name; + }; + +} + +// This core information can be looked using the ArchSpec::Core as the index +static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = +{ + { 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_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_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" }, + + + { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "ppc" }, + { 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" }, + + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_generic , "ppc64" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_ppc970_64 , "ppc970-64" }, + + { 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, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64 , "x86_64" }, + { 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" } +}; + +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; +}; + + +size_t +ArchSpec::AutoComplete (const char *name, StringList &matches) +{ + uint32_t i; + if (name && name[0]) + { + for (i = 0; i < ArchSpec::kNumCores; ++i) + { + if (NameMatches(g_core_definitions[i].name, eNameMatchStartsWith, name)) + matches.AppendString (g_core_definitions[i].name); + } + } + else + { + for (i = 0; i < ArchSpec::kNumCores; ++i) + matches.AppendString (g_core_definitions[i].name); + } + return matches.GetSize(); +} + + + +#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::CPUTypeARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_xscale , llvm::MachO::CPUTypeARM , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumb , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPUTypePowerPC , 1 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPUTypePowerPC , 2 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPUTypePowerPC , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPUTypePowerPC , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPUTypePowerPC , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPUTypePowerPC , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPUTypePowerPC , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPUTypePowerPC , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPUTypePowerPC , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPUTypePowerPC , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPUTypePowerPC , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPUTypePowerPC , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPUTypePowerPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPUTypePowerPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPUTypeI386 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPUTypeI386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_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::CPUArchABI64 , 0 , 0xFF000000u, 0x00000000u } +}; +static const ArchDefinition g_macho_arch_def = { + eArchTypeMachO, + sizeof(g_macho_arch_entries)/sizeof(g_macho_arch_entries[0]), + 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_486 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 486 (deprecated) + { ArchSpec::eCore_ppc_generic , llvm::ELF::EM_PPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC + { 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_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 +}; + +static const ArchDefinition g_elf_arch_def = { + eArchTypeELF, + sizeof(g_elf_arch_entries)/sizeof(g_elf_arch_entries[0]), + g_elf_arch_entries, + "elf", +}; + +//===----------------------------------------------------------------------===// +// Table of all ArchDefinitions +static const ArchDefinition *g_arch_definitions[] = { + &g_macho_arch_def, + &g_elf_arch_def +}; + +static const size_t k_num_arch_definitions = + sizeof(g_arch_definitions) / sizeof(g_arch_definitions[0]); + +//===----------------------------------------------------------------------===// +// 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 NULL; +} + +// Get an architecture definition by name. +static const CoreDefinition * +FindCoreDefinition (llvm::StringRef name) +{ + for (unsigned int i = 0; i < ArchSpec::kNumCores; ++i) + { + if (name.equals_lower(g_core_definitions[i].name)) + return &g_core_definitions[i]; + } + return NULL; +} + +static inline const CoreDefinition * +FindCoreDefinition (ArchSpec::Core core) +{ + if (core >= 0 && core < ArchSpec::kNumCores) + return &g_core_definitions[core]; + return NULL; +} + +// Get a definition entry by cpu type and subtype. +static const ArchDefinitionEntry * +FindArchDefinitionEntry (const ArchDefinition *def, uint32_t cpu, uint32_t sub) +{ + if (def == NULL) + return NULL; + + 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 NULL; +} + +static const ArchDefinitionEntry * +FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) +{ + if (def == NULL) + return NULL; + + const ArchDefinitionEntry *entries = def->entries; + for (size_t i = 0; i < def->num_entries; ++i) + { + if (entries[i].core == core) + return &entries[i]; + } + return NULL; +} + +//===----------------------------------------------------------------------===// +// Constructors and destructors. + +ArchSpec::ArchSpec() : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ +} + +ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + if (triple_cstr) + SetTriple(triple_cstr, platform); +} + + +ArchSpec::ArchSpec (const char *triple_cstr) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + if (triple_cstr) + SetTriple(triple_cstr); +} + +ArchSpec::ArchSpec(const llvm::Triple &triple) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + SetTriple(triple); +} + +ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : + m_triple (), + m_core (kCore_invalid), + m_byte_order (eByteOrderInvalid) +{ + SetArchitecture (arch_type, cpu, subtype); +} + +ArchSpec::~ArchSpec() +{ +} + +//===----------------------------------------------------------------------===// +// 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; + } + return *this; +} + +void +ArchSpec::Clear() +{ + m_triple = llvm::Triple(); + m_core = kCore_invalid; + m_byte_order = eByteOrderInvalid; +} + +//===----------------------------------------------------------------------===// +// Predicates. + + +const char * +ArchSpec::GetArchitectureName () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->name; + return "unknown"; +} + +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; +} + +llvm::Triple::ArchType +ArchSpec::GetMachine () const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + return core_def->machine; + + return llvm::Triple::UnknownArch; +} + +uint32_t +ArchSpec::GetAddressByteSize() const +{ + const CoreDefinition *core_def = FindCoreDefinition (m_core); + if (core_def) + 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; +} + +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; + + 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(); + } + + + return IsValid(); +} + +static bool +ParseMachCPUDashSubtypeTriple (const char *triple_cstr, ArchSpec &arch) +{ + // Accept "12-10" or "12.10" as cpu type/subtype + if (isdigit(triple_cstr[0])) + { + char *end = NULL; + errno = 0; + uint32_t cpu = (uint32_t)::strtoul (triple_cstr, &end, 0); + if (errno == 0 && cpu != 0 && end && ((*end == '-') || (*end == '.'))) + { + errno = 0; + uint32_t sub = (uint32_t)::strtoul (end + 1, &end, 0); + if (errno == 0 && end && ((*end == '-') || (*end == '.') || (*end == '\0'))) + { + if (arch.SetArchitecture (eArchTypeMachO, cpu, sub)) + { + if (*end == '-') + { + llvm::StringRef vendor_os (end + 1); + size_t dash_pos = vendor_os.find('-'); + if (dash_pos != llvm::StringRef::npos) + { + llvm::StringRef vendor_str(vendor_os.substr(0, dash_pos)); + arch.GetTriple().setVendorName(vendor_str); + const size_t vendor_start_pos = dash_pos+1; + dash_pos = vendor_os.find('-', vendor_start_pos); + if (dash_pos == llvm::StringRef::npos) + { + if (vendor_start_pos < vendor_os.size()) + arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos)); + } + else + { + arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos, dash_pos - vendor_start_pos)); + } + } + } + return true; + } + } + } + } + return false; +} +bool +ArchSpec::SetTriple (const char *triple_cstr) +{ + if (triple_cstr && triple_cstr[0]) + { + if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) + return true; + + llvm::StringRef triple_stref (triple_cstr); + if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) + { + // Special case for the current host default architectures... + if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + } + else + { + std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); + triple_stref = normalized_triple_sstr; + SetTriple (llvm::Triple (triple_stref)); + } + } + else + Clear(); + return IsValid(); +} + +bool +ArchSpec::SetTriple (const char *triple_cstr, Platform *platform) +{ + if (triple_cstr && triple_cstr[0]) + { + if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) + return true; + + llvm::StringRef triple_stref (triple_cstr); + if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) + { + // Special case for the current host default architectures... + if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); + else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) + *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + } + else + { + ArchSpec raw_arch (triple_cstr); + + std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); + triple_stref = normalized_triple_sstr; + llvm::Triple normalized_triple (triple_stref); + + const bool os_specified = normalized_triple.getOSName().size() > 0; + const bool vendor_specified = normalized_triple.getVendorName().size() > 0; + const bool env_specified = normalized_triple.getEnvironmentName().size() > 0; + + // If we got an arch only, then default the vendor, os, environment + // to match the platform if one is supplied + if (!(os_specified || vendor_specified || env_specified)) + { + if (platform) + { + // If we were given a platform, use the platform's system + // architecture. If this is not available (might not be + // connected) use the first supported architecture. + ArchSpec compatible_arch; + if (platform->IsCompatibleArchitecture (raw_arch, false, &compatible_arch)) + { + if (compatible_arch.IsValid()) + { + const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); + if (!vendor_specified) + normalized_triple.setVendor(compatible_triple.getVendor()); + if (!os_specified) + normalized_triple.setOS(compatible_triple.getOS()); + if (!env_specified && compatible_triple.getEnvironmentName().size()) + normalized_triple.setEnvironment(compatible_triple.getEnvironment()); + } + } + else + { + *this = raw_arch; + return IsValid(); + } + } + else + { + // No platform specified, fall back to the host system for + // the default vendor, os, and environment. + llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple()); + if (!vendor_specified) + normalized_triple.setVendor(host_triple.getVendor()); + if (!vendor_specified) + normalized_triple.setOS(host_triple.getOS()); + if (!env_specified && host_triple.getEnvironmentName().size()) + normalized_triple.setEnvironment(host_triple.getEnvironment()); + } + } + SetTriple (normalized_triple); + } + } + else + Clear(); + return IsValid(); +} + +bool +ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub) +{ + 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); + + switch (core_def->machine) + { + case llvm::Triple::arm: + case llvm::Triple::thumb: + m_triple.setOS (llvm::Triple::IOS); + break; + + case llvm::Triple::x86: + case llvm::Triple::x86_64: + default: + m_triple.setOS (llvm::Triple::MacOSX); + break; + } + } + 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); + } + } + } + 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); +} + +bool +ArchSpec::IsEqualTo (const ArchSpec& rhs, bool exact_match) const +{ + 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) + { + if (exact_match) + { + 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) + { + if (exact_match) + { + 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(); + + if (lhs_triple_env != rhs_triple_env) + { + // Only fail if both environment types are not unknown + if (lhs_triple_env != llvm::Triple::UnknownEnvironment && + rhs_triple_env != llvm::Triple::UnknownEnvironment) + return false; + } + return true; + } + return false; +} + +//===----------------------------------------------------------------------===// +// 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::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_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_armv7m: + case ArchSpec::eCore_arm_armv7em: + case ArchSpec::eCore_arm_armv7f: + case ArchSpec::eCore_arm_armv7k: + case ArchSpec::eCore_arm_armv7s: + if (!enforce_exact_match) + { + try_inverse = false; + if (core2 == ArchSpec::eCore_arm_armv7) + 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; +} diff --git a/source/Core/Baton.cpp b/source/Core/Baton.cpp new file mode 100644 index 00000000000..8bed01be883 --- /dev/null +++ b/source/Core/Baton.cpp @@ -0,0 +1,24 @@ +//===-- Baton.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Baton.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +Baton::GetDescription (Stream *s, lldb::DescriptionLevel level) const +{ +} diff --git a/source/Core/Broadcaster.cpp b/source/Core/Broadcaster.cpp new file mode 100644 index 00000000000..5af7497c8da --- /dev/null +++ b/source/Core/Broadcaster.cpp @@ -0,0 +1,499 @@ +//===-- Broadcaster.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Broadcaster.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamString.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +Broadcaster::Broadcaster (BroadcasterManager *manager, const char *name) : + m_broadcaster_name (name), + m_listeners (), + m_listeners_mutex (Mutex::eMutexTypeRecursive), + m_hijacking_listeners(), + m_hijacking_masks(), + m_manager (manager) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Broadcaster::Broadcaster(\"%s\")", this, m_broadcaster_name.AsCString()); + +} + +Broadcaster::~Broadcaster() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Broadcaster::~Broadcaster(\"%s\")", this, m_broadcaster_name.AsCString()); + + Clear(); +} + +void +Broadcaster::CheckInWithManager () +{ + if (m_manager != NULL) + { + m_manager->SignUpListenersForBroadcaster(*this); + } +} + +void +Broadcaster::Clear() +{ + Mutex::Locker listeners_locker(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. + + collection::iterator pos, end = m_listeners.end(); + for (pos = m_listeners.begin(); pos != end; ++pos) + pos->first->BroadcasterWillDestruct (this); + + m_listeners.clear(); +} +const ConstString & +Broadcaster::GetBroadcasterName () +{ + return m_broadcaster_name; +} + +bool +Broadcaster::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 (m_broadcaster_name.GetCString()); + s.PutChar('.'); + } + s.PutCString(pos->second.c_str()); + ++num_names_added; + } + } + } + } + return num_names_added > 0; +} + +void +Broadcaster::AddInitialEventsToListener (Listener *listener, uint32_t requested_events) +{ + +} + +uint32_t +Broadcaster::AddListener (Listener* listener, uint32_t event_mask) +{ + if (listener == NULL) + return 0; + + Mutex::Locker locker(m_listeners_mutex); + collection::iterator pos, end = m_listeners.end(); + + collection::iterator existing_pos = end; + // See if we already have this listener, and if so, update its mask + uint32_t taken_event_types = 0; + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->first == listener) + existing_pos = pos; + // For now don't descriminate on who gets what + // FIXME: Implement "unique listener for this bit" mask + // taken_event_types |= pos->second; + } + + // Each event bit in a Broadcaster object can only be used + // by one listener + uint32_t available_event_types = ~taken_event_types & event_mask; + + if (available_event_types) + { + // If we didn't find our listener, add it + if (existing_pos == end) + { + // Grant a new listener the available event bits + m_listeners.push_back(std::make_pair(listener, available_event_types)); + } + else + { + // Grant the existing listener the available event bits + existing_pos->second |= available_event_types; + } + + // Individual broadcasters decide whether they have outstanding data when a + // listener attaches, and insert it into the listener with this method. + + AddInitialEventsToListener (listener, available_event_types); + } + + // Return the event bits that were granted to the listener + return available_event_types; +} + +bool +Broadcaster::EventTypeHasListeners (uint32_t event_type) +{ + Mutex::Locker locker (m_listeners_mutex); + + if (m_hijacking_listeners.size() > 0 && event_type & m_hijacking_masks.back()) + return true; + + if (m_listeners.empty()) + return false; + + collection::iterator pos, end = m_listeners.end(); + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->second & event_type) + return true; + } + return false; +} + +bool +Broadcaster::RemoveListener (Listener* listener, uint32_t event_mask) +{ + Mutex::Locker locker(m_listeners_mutex); + collection::iterator pos, end = m_listeners.end(); + // See if we already have this listener, and if so, update its mask + for (pos = m_listeners.begin(); pos != end; ++pos) + { + if (pos->first == listener) + { + // Relinquish all event bits in "event_mask" + pos->second &= ~event_mask; + // If all bits have been relinquished then remove this listener + if (pos->second == 0) + m_listeners.erase (pos); + return true; + } + } + return false; +} + +void +Broadcaster::BroadcastEvent (EventSP &event_sp) +{ + return PrivateBroadcastEvent (event_sp, false); +} + +void +Broadcaster::BroadcastEventIfUnique (EventSP &event_sp) +{ + return PrivateBroadcastEvent (event_sp, true); +} + +void +Broadcaster::PrivateBroadcastEvent (EventSP &event_sp, bool unique) +{ + // Can't add a NULL event... + if (event_sp.get() == NULL) + return; + + // Update the broadcaster on this event + event_sp->SetBroadcaster (this); + + const uint32_t event_type = event_sp->GetType(); + + Mutex::Locker event_types_locker(m_listeners_mutex); + + Listener *hijacking_listener = NULL; + if (!m_hijacking_listeners.empty()) + { + assert (!m_hijacking_masks.empty()); + hijacking_listener = m_hijacking_listeners.back(); + if ((event_type & m_hijacking_masks.back()) == 0) + hijacking_listener = NULL; + } + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + StreamString event_description; + event_sp->Dump (&event_description); + log->Printf ("%p Broadcaster(\"%s\")::BroadcastEvent (event_sp = {%s}, unique =%i) hijack = %p", + this, + m_broadcaster_name.AsCString(""), + event_description.GetData(), + unique, + hijacking_listener); + } + + if (hijacking_listener) + { + if (unique && hijacking_listener->PeekAtNextEventForBroadcasterWithType (this, event_type)) + return; + hijacking_listener->AddEvent (event_sp); + } + else + { + collection::iterator pos, end = m_listeners.end(); + + + // Iterate through all listener/mask pairs + for (pos = m_listeners.begin(); pos != end; ++pos) + { + // If the listener's mask matches any bits that we just set, then + // put the new event on its event queue. + if (event_type & pos->second) + { + if (unique && pos->first->PeekAtNextEventForBroadcasterWithType (this, event_type)) + continue; + pos->first->AddEvent (event_sp); + } + } + } +} + +void +Broadcaster::BroadcastEvent (uint32_t event_type, EventData *event_data) +{ + EventSP event_sp (new Event (event_type, event_data)); + PrivateBroadcastEvent (event_sp, false); +} + +void +Broadcaster::BroadcastEventIfUnique (uint32_t event_type, EventData *event_data) +{ + EventSP event_sp (new Event (event_type, event_data)); + PrivateBroadcastEvent (event_sp, true); +} + +bool +Broadcaster::HijackBroadcaster (Listener *listener, uint32_t event_mask) +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + log->Printf ("%p Broadcaster(\"%s\")::HijackBroadcaster (listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.push_back(listener); + m_hijacking_masks.push_back(event_mask); + return true; +} + +void +Broadcaster::RestoreBroadcaster () +{ + Mutex::Locker event_types_locker(m_listeners_mutex); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + { + Listener *listener = m_hijacking_listeners.back(); + log->Printf ("%p Broadcaster(\"%s\")::RestoreBroadcaster (about to pop listener(\"%s\")=%p)", + this, + m_broadcaster_name.AsCString(""), + listener->m_name.c_str(), + listener); + } + m_hijacking_listeners.pop_back(); + m_hijacking_masks.pop_back(); +} + +ConstString & +Broadcaster::GetBroadcasterClass() const +{ + static ConstString class_name ("lldb.anonymous"); + return class_name; +} + +BroadcastEventSpec::BroadcastEventSpec (const BroadcastEventSpec &rhs) : + m_broadcaster_class (rhs.m_broadcaster_class), + m_event_bits (rhs.m_event_bits) +{ +} + +bool +BroadcastEventSpec::operator< (const BroadcastEventSpec &rhs) const +{ + if (GetBroadcasterClass() == rhs.GetBroadcasterClass()) + { + return GetEventBits() < rhs.GetEventBits(); + } + else + { + return GetBroadcasterClass() < rhs.GetBroadcasterClass(); + } +} + +const BroadcastEventSpec & +BroadcastEventSpec::operator= (const BroadcastEventSpec &rhs) +{ + m_broadcaster_class = rhs.m_broadcaster_class; + m_event_bits = rhs.m_event_bits; + return *this; +} + +BroadcasterManager::BroadcasterManager() : + m_manager_mutex(Mutex::eMutexTypeRecursive) +{ + +} + +uint32_t +BroadcasterManager::RegisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec) +{ + Mutex::Locker locker(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)); + m_listeners.insert(&listener); + } + + return available_bits; +} + +bool +BroadcasterManager::UnregisterListenerForEvents (Listener &listener, BroadcastEventSpec event_spec) +{ + Mutex::Locker locker(m_manager_mutex); + bool removed_some = false; + + if (m_listeners.erase(&listener) == 0) + return false; + + ListenerMatchesAndSharedBits predicate (event_spec, listener); + std::vector 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 (1) + { + collection::iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, predicate); + if (iter == end_iter) + { + break; + } + else + { + 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)); + } + + return removed_some; +} + +Listener * +BroadcasterManager::GetListenerForEventSpec (BroadcastEventSpec event_spec) const +{ + Mutex::Locker locker(*(const_cast (&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; + else + return NULL; +} + +void +BroadcasterManager::RemoveListener (Listener &listener) +{ + Mutex::Locker locker(m_manager_mutex); + ListenerMatches predicate (listener); + + + if (m_listeners.erase (&listener) == 0) + return; + + while (1) + { + collection::iterator iter, end_iter = m_event_map.end(); + iter = find_if (m_event_map.begin(), end_iter, predicate); + if (iter == end_iter) + break; + else + m_event_map.erase(iter); + } +} + +void +BroadcasterManager::SignUpListenersForBroadcaster (Broadcaster &broadcaster) +{ + Mutex::Locker locker(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 () +{ + Mutex::Locker locker(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); + m_listeners.clear(); + m_event_map.clear(); + +} diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp new file mode 100644 index 00000000000..7f40e652020 --- /dev/null +++ b/source/Core/Communication.cpp @@ -0,0 +1,431 @@ +//===-- Communication.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Connection.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/Event.h" +#include "lldb/Host/Host.h" +#include + +using namespace lldb; +using namespace lldb_private; + +ConstString & +Communication::GetStaticBroadcasterClass () +{ + static ConstString class_name ("lldb.communication"); + return class_name; +} + +//---------------------------------------------------------------------- +// Constructor +//---------------------------------------------------------------------- +Communication::Communication(const char *name) : + Broadcaster (NULL, name), + m_connection_sp (), + m_read_thread (LLDB_INVALID_HOST_THREAD), + m_read_thread_enabled (false), + m_bytes(), + m_bytes_mutex (Mutex::eMutexTypeRecursive), + m_write_mutex (Mutex::eMutexTypeNormal), + m_callback (NULL), + m_callback_baton (NULL), + 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"); + + CheckInWithManager(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Communication::~Communication() +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_COMMUNICATION, + "%p Communication::~Communication (name = %s)", + this, m_broadcaster_name.AsCString("")); + Clear(); +} + +void +Communication::Clear() +{ + SetReadThreadBytesReceivedCallback (NULL, NULL); + Disconnect (NULL); + StopReadThread (NULL); +} + +ConnectionStatus +Communication::Connect (const char *url, Error *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.get()) + return connection_sp->Connect (url, error_ptr); + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + return eConnectionStatusNoConnection; +} + +ConnectionStatus +Communication::Disconnect (Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, "%p Communication::Disconnect ()", this); + + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + { + 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 disconnnect. + //connection_sp.reset(); + return status; + } + return eConnectionStatusNoConnection; +} + +bool +Communication::IsConnected () const +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->IsConnected (); + return false; +} + +bool +Communication::HasConnection () const +{ + return m_connection_sp.get() != NULL; +} + +size_t +Communication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, ConnectionStatus &status, Error *error_ptr) +{ + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::Read (dst = %p, dst_len = %" PRIu64 ", timeout = %u usec) connection = %p", + this, + dst, + (uint64_t)dst_len, + timeout_usec, + 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_usec == 0) + { + status = eConnectionStatusSuccess; + return cached_bytes; + } + + if (m_connection_sp.get() == NULL) + { + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; + } + // Set the timeout appropriately + TimeValue timeout_time; + if (timeout_usec != UINT32_MAX) + { + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithMicroSeconds (timeout_usec); + } + + Listener listener ("Communication::Read"); + listener.StartListeningForEvents (this, eBroadcastBitReadThreadGotBytes | eBroadcastBitReadThreadDidExit); + EventSP event_sp; + while (listener.WaitForEvent (timeout_time.IsValid() ? &timeout_time : NULL, event_sp)) + { + const uint32_t event_type = event_sp->GetType(); + if (event_type & eBroadcastBitReadThreadGotBytes) + { + return GetCachedBytes (dst, dst_len); + } + + if (event_type & eBroadcastBitReadThreadDidExit) + { + Disconnect (NULL); + break; + } + } + return 0; + } + + // We aren't using a read thread, just read the data synchronously in this + // thread. + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + { + return connection_sp->Read (dst, dst_len, timeout_usec, status, error_ptr); + } + + if (error_ptr) + error_ptr->SetErrorString("Invalid connection."); + status = eConnectionStatusNoConnection; + return 0; +} + + +size_t +Communication::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + + Mutex::Locker locker(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.get()) + 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 (Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + + if (IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::StartReadThread ()", this); + + + char thread_name[1024]; + snprintf(thread_name, sizeof(thread_name), "", m_broadcaster_name.AsCString()); + + m_read_thread_enabled = true; + m_read_thread = Host::ThreadCreate (thread_name, Communication::ReadThread, this, error_ptr); + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + m_read_thread_enabled = false; + return m_read_thread_enabled; +} + +bool +Communication::StopReadThread (Error *error_ptr) +{ + if (!IS_VALID_LLDB_HOST_THREAD(m_read_thread)) + return true; + + lldb_private::LogIfAnyCategoriesSet (LIBLLDB_LOG_COMMUNICATION, + "%p Communication::StopReadThread ()", this); + + m_read_thread_enabled = false; + + BroadcastEvent (eBroadcastBitReadThreadShouldExit, NULL); + + //Host::ThreadCancel (m_read_thread, error_ptr); + + bool status = Host::ThreadJoin (m_read_thread, NULL, error_ptr); + m_read_thread = LLDB_INVALID_HOST_THREAD; + return status; +} + + +size_t +Communication::GetCachedBytes (void *dst, size_t dst_len) +{ + Mutex::Locker locker(m_bytes_mutex); + if (m_bytes.size() > 0) + { + // If DST is NULL and we have a thread, then return the number + // of bytes that are available so the caller can call again + if (dst == NULL) + return m_bytes.size(); + + const size_t len = std::min(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 == NULL || 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 != NULL && len > 0) + { + Mutex::Locker locker(m_bytes_mutex); + m_bytes.append ((const char *)bytes, len); + if (broadcast) + BroadcastEventIfUnique (eBroadcastBitReadThreadGotBytes); + } +} + +size_t +Communication::ReadFromConnection (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + lldb::ConnectionSP connection_sp (m_connection_sp); + if (connection_sp.get()) + return connection_sp->Read (dst, dst_len, timeout_usec, status, error_ptr); + return 0; +} + +bool +Communication::ReadThreadIsRunning () +{ + return m_read_thread_enabled; +} + +void * +Communication::ReadThread (void *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]; + + Error error; + ConnectionStatus status = eConnectionStatusSuccess; + bool done = false; + while (!done && comm->m_read_thread_enabled) + { + size_t bytes_read = comm->ReadFromConnection (buf, sizeof(buf), 5 * TimeValue::MicroSecPerSec, 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: + if (comm->GetCloseOnEOF()) + done = true; + break; + case eConnectionStatusNoConnection: // No connection + case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection + done = true; + // Fall through... + case eConnectionStatusError: // Check GetError() for details + case eConnectionStatusTimedOut: // Request timed out + if (log) + error.LogIfError (log, + "%p Communication::ReadFromConnection () => status = %s", + p, + Communication::ConnectionStatusAsCString (status)); + break; + } + } + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMUNICATION); + if (log) + log->Printf ("%p Communication::ReadThread () thread exiting...", p); + + // Let clients know that this thread is exiting + comm->BroadcastEvent (eBroadcastBitReadThreadDidExit); + return NULL; +} + +void +Communication::SetReadThreadBytesReceivedCallback +( + ReadThreadBytesReceived callback, + void *callback_baton +) +{ + m_callback = callback; + m_callback_baton = callback_baton; +} + +void +Communication::SetConnection (Connection *connection) +{ + Disconnect (NULL); + StopReadThread(NULL); + 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"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "ConnectionStatus = %i", status); + return unknown_state_string; +} diff --git a/source/Core/Connection.cpp b/source/Core/Connection.cpp new file mode 100644 index 00000000000..3c9bb8b1b7e --- /dev/null +++ b/source/Core/Connection.cpp @@ -0,0 +1,24 @@ +//===-- Connection.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Connection.h" + +using namespace lldb_private; + +Connection::Connection () +{ +} + +Connection::~Connection () +{ +} diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Core/ConnectionFileDescriptor.cpp new file mode 100644 index 00000000000..e320bda2fcd --- /dev/null +++ b/source/Core/ConnectionFileDescriptor.cpp @@ -0,0 +1,1528 @@ +//===-- ConnectionFileDescriptor.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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/Core/ConnectionFileDescriptor.h" + +// C Includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +#if defined(__APPLE__) +#include "llvm/ADT/SmallVector.h" +#endif +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +DecodeHostAndPort (const char *host_and_port, + std::string &host_str, + std::string &port_str, + int32_t& port, + Error *error_ptr) +{ + static RegularExpression g_regex ("([^:]+):([0-9]+)"); + RegularExpression::Match regex_match(2); + if (g_regex.Execute (host_and_port, ®ex_match)) + { + if (regex_match.GetMatchAtIndex (host_and_port, 1, host_str) && + regex_match.GetMatchAtIndex (host_and_port, 2, port_str)) + { + port = Args::StringToSInt32 (port_str.c_str(), INT32_MIN); + if (port != INT32_MIN) + { + if (error_ptr) + error_ptr->Clear(); + return true; + } + } + } + host_str.clear(); + port_str.clear(); + port = INT32_MIN; + if (error_ptr) + error_ptr->SetErrorStringWithFormat("invalid host:port specification: '%s'", host_and_port); + return false; +} + +ConnectionFileDescriptor::ConnectionFileDescriptor () : + Connection(), + m_fd_send (-1), + m_fd_recv (-1), + m_fd_send_type (eFDTypeFile), + m_fd_recv_type (eFDTypeFile), + m_udp_send_sockaddr (), + m_should_close_fd (false), + m_socket_timeout_usec(0), + m_pipe_read(-1), + m_pipe_write(-1), + m_mutex (Mutex::eMutexTypeRecursive), + m_shutting_down (false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor ()", this); +} + +ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : + Connection(), + m_fd_send (fd), + m_fd_recv (fd), + m_fd_send_type (eFDTypeFile), + m_fd_recv_type (eFDTypeFile), + m_udp_send_sockaddr (), + m_should_close_fd (owns_fd), + m_socket_timeout_usec(0), + m_pipe_read(-1), + m_pipe_write(-1), + m_mutex (Mutex::eMutexTypeRecursive), + m_shutting_down (false) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor (fd = %i, owns_fd = %i)", this, fd, owns_fd); + OpenCommandPipe (); +} + + +ConnectionFileDescriptor::~ConnectionFileDescriptor () +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION | LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::~ConnectionFileDescriptor ()", this); + Disconnect (NULL); + CloseCommandPipe (); +} + +void +ConnectionFileDescriptor::OpenCommandPipe () +{ + CloseCommandPipe(); + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + // Make the command file descriptor here: + int filedes[2]; + int result = pipe (filedes); + if (result != 0) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor () - could not make pipe: %s", + this, + strerror(errno)); + } + else + { + m_pipe_read = filedes[0]; + m_pipe_write = filedes[1]; + } +} + +void +ConnectionFileDescriptor::CloseCommandPipe () +{ + if (m_pipe_read != -1) + { + close (m_pipe_read); + m_pipe_read = -1; + } + + if (m_pipe_write != -1) + { + close (m_pipe_write); + m_pipe_write = -1; + } +} + +bool +ConnectionFileDescriptor::IsConnected () const +{ + return m_fd_send >= 0 || m_fd_recv >= 0; +} + +ConnectionStatus +ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) +{ + Mutex::Locker locker (m_mutex); + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Connect (url = '%s')", this, s); + + OpenCommandPipe(); + + if (s && s[0]) + { + char *end = NULL; + if (strstr(s, "listen://")) + { + // listen://HOST:PORT + unsigned long listen_port = ::strtoul(s + strlen("listen://"), &end, 0); + return SocketListen (listen_port, error_ptr); + } + else if (strstr(s, "unix-accept://")) + { + // unix://SOCKNAME + return NamedSocketAccept (s + strlen("unix-accept://"), error_ptr); + } + else if (strstr(s, "connect://")) + { + return ConnectTCP (s + strlen("connect://"), error_ptr); + } + else if (strstr(s, "tcp-connect://")) + { + return ConnectTCP (s + strlen("tcp-connect://"), error_ptr); + } + else if (strstr(s, "udp://")) + { + return ConnectUDP (s + strlen("udp://"), error_ptr); + } + else if (strstr(s, "fd://")) + { + // Just passing a native file descriptor within this current process + // that is already opened (possibly from a service or other source). + s += strlen ("fd://"); + bool success = false; + m_fd_send = m_fd_recv = Args::StringToSInt32 (s, -1, 0, &success); + + if (success) + { + // 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 (m_fd_send, F_GETFL, 0); + if (flags == -1 || errno == EBADF) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("stale file descriptor: %s", s); + m_fd_send = m_fd_recv = -1; + return eConnectionStatusError; + } + else + { + // 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 = GetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, resuse) == 0; + if (is_socket) + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + // 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. + m_should_close_fd = false; + return eConnectionStatusSuccess; + } + } + + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("invalid file descriptor: \"fd://%s\"", s); + m_fd_send = m_fd_recv = -1; + return eConnectionStatusError; + } + else if (strstr(s, "file://")) + { + // file:///PATH + const char *path = s + strlen("file://"); + do + { + m_fd_send = m_fd_recv = ::open (path, O_RDWR); + } while (m_fd_send == -1 && errno == EINTR); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + if (::isatty(m_fd_send)) + { + // Set up serial terminal emulation + struct termios options; + ::tcgetattr (m_fd_send, &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; + + ::tcsetattr (m_fd_send, TCSANOW, &options); + } + + int flags = ::fcntl (m_fd_send, F_GETFL, 0); + if (flags >= 0) + { + if ((flags & O_NONBLOCK) == 0) + { + flags |= O_NONBLOCK; + ::fcntl (m_fd_send, F_SETFL, flags); + } + } + m_should_close_fd = true; + return eConnectionStatusSuccess; + } + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::Disconnect (Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect ()", this); + + ConnectionStatus status = eConnectionStatusSuccess; + + if (m_fd_send < 0 && m_fd_recv < 0) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Nothing to disconnect", this); + return eConnectionStatusSuccess; + } + + // 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; + + Mutex::Locker locker; + bool got_lock= locker.TryLock (m_mutex); + + if (!got_lock) + { + if (m_pipe_write != -1 ) + { + write (m_pipe_write, "q", 1); + close (m_pipe_write); + m_pipe_write = -1; + } + locker.Lock (m_mutex); + } + + if (m_should_close_fd == true) + { + if (m_fd_send == m_fd_recv) + { + status = Close (m_fd_send, error_ptr); + } + else + { + // File descriptors are the different, close both if needed + if (m_fd_send >= 0) + status = Close (m_fd_send, error_ptr); + if (m_fd_recv >= 0) + { + ConnectionStatus recv_status = Close (m_fd_recv, error_ptr); + if (status == eConnectionStatusSuccess) + status = recv_status; + } + } + } + + // Now set all our descriptors to invalid values. + + m_fd_send = m_fd_recv = -1; + + if (status != eConnectionStatusSuccess) + { + + return status; + } + + m_shutting_down = false; + return eConnectionStatusSuccess; +} + +size_t +ConnectionFileDescriptor::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ")...", + this, m_fd_recv, dst, (uint64_t)dst_len); + + Mutex::Locker locker; + bool got_lock = locker.TryLock (m_mutex); + if (!got_lock) + { + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () failed to get the connection lock.", + this); + if (error_ptr) + error_ptr->SetErrorString ("failed to get the connection lock for read."); + + status = eConnectionStatusTimedOut; + return 0; + } + else if (m_shutting_down) + return eConnectionStatusError; + + ssize_t bytes_read = 0; + + status = BytesAvailable (timeout_usec, error_ptr); + if (status == eConnectionStatusSuccess) + { + do + { + bytes_read = ::read (m_fd_recv, dst, dst_len); + } while (bytes_read < 0 && errno == EINTR); + } + + if (status != eConnectionStatusSuccess) + return 0; + + Error error; + if (bytes_read == 0) + { + error.Clear(); // End-of-file. Do not automatically close; pass along for the end-of-file handlers. + status = eConnectionStatusEndOfFile; + } + else if (bytes_read < 0) + { + error.SetErrorToErrno(); + } + else + { + error.Clear(); + } + + if (log) + log->Printf ("%p ConnectionFileDescriptor::Read () ::read (fd = %i, dst = %p, dst_len = %" PRIu64 ") => %" PRIi64 ", error = %s", + this, + m_fd_recv, + dst, + (uint64_t)dst_len, + (int64_t)bytes_read, + error.AsCString()); + + 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_fd_recv_type == eFDTypeSocket || m_fd_recv_type == eFDTypeSocketUDP) + 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; + } + + return 0; + } + return bytes_read; +} + +size_t +ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Write (src = %p, src_len = %" PRIu64 ")", this, src, (uint64_t)src_len); + + if (!IsConnected ()) + { + if (error_ptr) + error_ptr->SetErrorString("not connected"); + status = eConnectionStatusNoConnection; + return 0; + } + + + Error error; + + ssize_t bytes_sent = 0; + + switch (m_fd_send_type) + { + case eFDTypeFile: // Other FD requireing read/write + do + { + bytes_sent = ::write (m_fd_send, src, src_len); + } while (bytes_sent < 0 && errno == EINTR); + break; + + case eFDTypeSocket: // Socket requiring send/recv + do + { + bytes_sent = ::send (m_fd_send, src, src_len, 0); + } while (bytes_sent < 0 && errno == EINTR); + break; + + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + assert (m_udp_send_sockaddr.GetFamily() != 0); + do + { + bytes_sent = ::sendto (m_fd_send, + src, + src_len, + 0, + m_udp_send_sockaddr, + m_udp_send_sockaddr.GetLength()); + } while (bytes_sent < 0 && errno == EINTR); + break; + } + + if (bytes_sent < 0) + error.SetErrorToErrno (); + else + error.Clear (); + + if (log) + { + switch (m_fd_send_type) + { + case eFDTypeFile: // Other FD requireing read/write + log->Printf ("%p ConnectionFileDescriptor::Write() ::write (fd = %i, src = %p, src_len = %" PRIu64 ") => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + + case eFDTypeSocket: // Socket requiring send/recv + log->Printf ("%p ConnectionFileDescriptor::Write() ::send (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + log->Printf ("%p ConnectionFileDescriptor::Write() ::sendto (socket = %i, src = %p, src_len = %" PRIu64 ", flags = 0) => %" PRIi64 " (error = %s)", + this, + m_fd_send, + src, + (uint64_t)src_len, + (int64_t)bytes_sent, + error.AsCString()); + break; + } + } + + 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; +} + + + +#if defined(__APPLE__) + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// - this 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: +// - Darwin only + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *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::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + tv_ptr = NULL; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds (timeout_usec); + tv = time_value.GetAsTimeVal(); + tv_ptr = &tv; + } + + // 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 int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + if (data_fd >= 0) + { + const bool have_pipe_fd = pipe_fd >= 0; + + while (data_fd == m_fd_recv) + { + const int nfds = std::max(data_fd, pipe_fd) + 1; + llvm::SmallVector read_fds; + read_fds.resize((nfds/FD_SETSIZE) + 1); + for (size_t i=0; iPrintf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, pipe_fd, tv_ptr); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, tv_ptr); + } + + const int num_set_fds = ::select (nfds, read_fds.data(), NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, pipe_fd, tv_ptr, num_set_fds, error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, tv_ptr, num_set_fds, error.AsCString()); + } + + 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 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 (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + // FD_ISSET is happy to deal with a something larger than + // a single fd_set. + if (FD_ISSET(data_fd, read_fds.data())) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, read_fds.data())) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#else + +// This ConnectionFileDescriptor::BytesAvailable() uses select(). +// +// PROS: +// - select is consistent across most unix platforms +// CONS: +// - 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. MacOSX +// uses kqueues, and there is a poll() based implementation below. + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *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::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + struct timeval *tv_ptr; + struct timeval tv; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + tv_ptr = NULL; + } + else + { + TimeValue time_value; + time_value.OffsetWithMicroSeconds (timeout_usec); + tv = time_value.GetAsTimeVal(); + tv_ptr = &tv; + } + + // 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 int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + if (data_fd >= 0) + { + // If this assert fires off on MacOSX, we will need to switch to using + // libdispatch to read from file descriptors because poll() is causing + // kernel panics and if we exceed FD_SETSIZE we will have no choice... + assert (data_fd < FD_SETSIZE); + + const bool have_pipe_fd = pipe_fd >= 0; + + if (have_pipe_fd) + { + assert (pipe_fd < FD_SETSIZE); + } + + while (data_fd == m_fd_recv) + { + fd_set read_fds; + FD_ZERO (&read_fds); + FD_SET (data_fd, &read_fds); + if (have_pipe_fd) + FD_SET (pipe_fd, &read_fds); + + const int nfds = std::max(data_fd, pipe_fd) + 1; + + Error error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, pipe_fd, tv_ptr); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p)...", + this, nfds, data_fd, tv_ptr); + } + + const int num_set_fds = ::select (nfds, &read_fds, NULL, NULL, tv_ptr); + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i, %i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, pipe_fd, tv_ptr, num_set_fds, error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::select (nfds=%i, fds={%i}, NULL, NULL, timeout=%p) => %d, error = %s", + this, nfds, data_fd, tv_ptr, num_set_fds, error.AsCString()); + } + + 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 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 (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (FD_ISSET(data_fd, &read_fds)) + return eConnectionStatusSuccess; + if (have_pipe_fd && FD_ISSET(pipe_fd, &read_fds)) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#endif + +#if 0 +#include + +// This ConnectionFileDescriptor::BytesAvailable() uses poll(). poll() should NOT +// be used on MacOSX as it has all sorts of restrictions on the types of file descriptors +// that it doesn't support. +// +// There may be some systems that properly support poll() that could use this +// implementation. I will let each system opt into this on their own. +// +// PROS: +// - no restrictions on the fd value that is used +// CONS: +// - varies wildly from platform to platform in its implementation restrictions + +ConnectionStatus +ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *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::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable (timeout_usec = %u)", this, timeout_usec); + int timeout_msec = 0; + if (timeout_usec == UINT32_MAX) + { + // Infinite wait... + timeout_msec = -1; + } + else if (timeout_usec == 0) + { + // Return immediately, don't wait + timeout_msec = 0; + } + else + { + // Convert usec to msec + timeout_msec = (timeout_usec + 999) / 1000; + } + + // 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 int data_fd = m_fd_recv; + const int pipe_fd = m_pipe_read; + + // Make sure the file descriptor can be used with select as it + // must be in range + if (data_fd >= 0) + { + const bool have_pipe_fd = pipe_fd >= 0; + struct pollfd fds[2] = + { + { data_fd, POLLIN, 0 }, + { pipe_fd, POLLIN, 0 } + }; + const int nfds = have_pipe_fd ? 2 : 1; + Error error; + while (data_fd == m_fd_recv) + { + const int num_set_fds = ::poll (fds, nfds, timeout_msec); + + if (num_set_fds < 0) + error.SetErrorToErrno(); + else + error.Clear(); + + if (error_ptr) + *error_ptr = error; + + if (log) + { + if (have_pipe_fd) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN},{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n", + this, + data_fd, + pipe_fd, + nfds, + timeout_msec, + num_set_fds, + error.AsCString()); + else + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() ::poll (fds={{%i,POLLIN}}, nfds=%i, timeout_ms=%i) => %d, error = %s\n", + this, + data_fd, + nfds, + timeout_msec, + num_set_fds, + error.AsCString()); + } + + 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 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 (num_set_fds == 0) + { + return eConnectionStatusTimedOut; + } + else if (num_set_fds > 0) + { + if (fds[0].revents & POLLIN) + return eConnectionStatusSuccess; + if (fds[1].revents & POLLIN) + { + // We got a command to exit. Read the data from that pipe: + char buffer[16]; + ssize_t bytes_read; + + do + { + bytes_read = ::read (pipe_fd, buffer, sizeof(buffer)); + } while (bytes_read < 0 && errno == EINTR); + assert (bytes_read == 1 && buffer[0] == 'q'); + + if (log) + log->Printf("%p ConnectionFileDescriptor::BytesAvailable() got data: %*s from the command channel.", + this, (int) bytes_read, buffer); + + return eConnectionStatusEndOfFile; + } + } + } + } + if (error_ptr) + error_ptr->SetErrorString("not connected"); + return eConnectionStatusLostConnection; +} + +#endif + +ConnectionStatus +ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) +{ + if (error_ptr) + error_ptr->Clear(); + bool success = true; + // Avoid taking a lock if we can + if (fd >= 0) + { + Mutex::Locker locker (m_mutex); + // Check the FD after the lock is taken to ensure only one thread + // can get into the close scope below + if (fd >= 0) + { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Close (fd = %i)", this,fd); + + success = ::close (fd) == 0; + // A reference to a FD was passed in, set it to an invalid value + fd = -1; + if (!success && error_ptr) + { + // Only set the error if we have been asked to since something else + // might have caused us to try and shut down the connection and may + // have already set the error. + error_ptr->SetErrorToErrno(); + } + } + } + if (success) + return eConnectionStatusSuccess; + else + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) +{ + ConnectionStatus result = eConnectionStatusError; + struct sockaddr_un saddr_un; + + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + + int listen_socket = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (listen_socket == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; +#if defined(__APPLE__) || defined(__FreeBSD__) + saddr_un.sun_len = SUN_LEN (&saddr_un); +#endif + + if (::bind (listen_socket, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) == 0) + { + if (::listen (listen_socket, 5) == 0) + { + m_fd_send = m_fd_recv = ::accept (listen_socket, NULL, 0); + if (m_fd_send > 0) + { + m_should_close_fd = true; + + if (error_ptr) + error_ptr->Clear(); + result = eConnectionStatusSuccess; + } + } + } + + if (result != eConnectionStatusSuccess) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + } + // We are done with the listen port + Close (listen_socket, NULL); + return result; +} + +ConnectionStatus +ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) +{ + Disconnect (NULL); + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + + // Open the socket that was passed in as an option + struct sockaddr_un saddr_un; + m_fd_send = m_fd_recv = ::socket (AF_UNIX, SOCK_STREAM, 0); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + saddr_un.sun_family = AF_UNIX; + ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); + saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; +#if defined(__APPLE__) || defined(__FreeBSD__) + saddr_un.sun_len = SUN_LEN (&saddr_un); +#endif + + if (::connect (m_fd_send, (struct sockaddr *)&saddr_un, SUN_LEN (&saddr_un)) < 0) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + return eConnectionStatusError; + } + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::SocketListen (port = %i)", this, listen_port_num); + + Disconnect (NULL); + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + int listen_port = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listen_port == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + // enable local address reuse + SetSocketOption (listen_port, SOL_SOCKET, SO_REUSEADDR, 1); + + SocketAddress localhost; + if (localhost.SetToLocalhost (AF_INET, listen_port_num)) + { + int err = ::bind (listen_port, localhost, localhost.GetLength()); + if (err == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + + err = ::listen (listen_port, 1); + if (err == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + + m_fd_send = m_fd_recv = ::accept (listen_port, NULL, 0); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Close (listen_port, NULL); + return eConnectionStatusError; + } + } + + // We are done with the listen port + Close (listen_port, NULL); + + m_should_close_fd = true; + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectTCP (const char *host_and_port, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectTCP (host/port = %s)", this, host_and_port); + Disconnect (NULL); + + m_fd_send_type = m_fd_recv_type = eFDTypeSocket; + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + return eConnectionStatusError; + + // Create the socket + m_fd_send = m_fd_recv = ::socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_fd_send == -1) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + return eConnectionStatusError; + } + + m_should_close_fd = true; + + // Enable local address reuse + SetSocketOption (m_fd_send, SOL_SOCKET, SO_REUSEADDR, 1); + + struct sockaddr_in sa; + ::memset (&sa, 0, sizeof (sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons (port); + + int inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); + + if (inet_pton_result <= 0) + { + struct hostent *host_entry = gethostbyname (host_str.c_str()); + if (host_entry) + host_str = ::inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list); + inet_pton_result = ::inet_pton (AF_INET, host_str.c_str(), &sa.sin_addr); + if (inet_pton_result <= 0) + { + + if (error_ptr) + { + if (inet_pton_result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->SetErrorStringWithFormat("invalid host string: '%s'", host_str.c_str()); + } + Disconnect (NULL); + + return eConnectionStatusError; + } + } + + if (-1 == ::connect (m_fd_send, (const struct sockaddr *)&sa, sizeof(sa))) + { + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + + return eConnectionStatusError; + } + + // Keep our TCP packets coming without any delays. + SetSocketOption (m_fd_send, IPPROTO_TCP, TCP_NODELAY, 1); + if (error_ptr) + error_ptr->Clear(); + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_ptr) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::ConnectUDP (host/port = %s)", this, host_and_port); + Disconnect (NULL); + + m_fd_send_type = m_fd_recv_type = eFDTypeSocketUDP; + + std::string host_str; + std::string port_str; + int32_t port = INT32_MIN; + if (!DecodeHostAndPort (host_and_port, host_str, port_str, port, error_ptr)) + return eConnectionStatusError; + + // Setup the receiving end of the UDP connection on this localhost + // on port zero. After we bind to port zero we can read the port. + m_fd_recv = ::socket (AF_INET, SOCK_DGRAM, 0); + if (m_fd_recv == -1) + { + // Socket creation failed... + if (error_ptr) + error_ptr->SetErrorToErrno(); + } + else + { + // Socket was created, now lets bind to the requested port + SocketAddress addr; + addr.SetToLocalhost (AF_INET, 0); + + if (::bind (m_fd_recv, addr, addr.GetLength()) == -1) + { + // Bind failed... + if (error_ptr) + error_ptr->SetErrorToErrno(); + Disconnect (NULL); + } + } + + if (m_fd_recv == -1) + return eConnectionStatusError; + + // At this point we have setup the recieve port, now we need to + // setup the UDP send socket + + struct addrinfo hints; + struct addrinfo *service_info_list = NULL; + + ::memset (&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_DGRAM; + int err = ::getaddrinfo (host_str.c_str(), port_str.c_str(), &hints, &service_info_list); + if (err != 0) + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat("getaddrinfo(%s, %s, &hints, &info) returned error %i (%s)", + host_str.c_str(), + port_str.c_str(), + err, + gai_strerror(err)); + Disconnect (NULL); + return eConnectionStatusError; + } + + for (struct addrinfo *service_info_ptr = service_info_list; + service_info_ptr != NULL; + service_info_ptr = service_info_ptr->ai_next) + { + m_fd_send = ::socket (service_info_ptr->ai_family, + service_info_ptr->ai_socktype, + service_info_ptr->ai_protocol); + + if (m_fd_send != -1) + { + m_udp_send_sockaddr = service_info_ptr; + break; + } + else + continue; + } + + :: freeaddrinfo (service_info_list); + + if (m_fd_send == -1) + { + Disconnect (NULL); + return eConnectionStatusError; + } + + if (error_ptr) + error_ptr->Clear(); + + m_should_close_fd = true; + return eConnectionStatusSuccess; +} + +#if defined(__MINGW32__) || defined(__MINGW64__) +typedef const char * set_socket_option_arg_type; +typedef char * get_socket_option_arg_type; +#else // #if defined(__MINGW32__) || defined(__MINGW64__) +typedef const void * set_socket_option_arg_type; +typedef void * get_socket_option_arg_type; +#endif // #if defined(__MINGW32__) || defined(__MINGW64__) + +int +ConnectionFileDescriptor::GetSocketOption(int fd, int level, int option_name, int &option_value) +{ + get_socket_option_arg_type option_value_p = static_cast(&option_value); + socklen_t option_value_size = sizeof(int); + return ::getsockopt(fd, level, option_name, option_value_p, &option_value_size); +} + +int +ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) +{ + set_socket_option_arg_type option_value_p = static_cast(&option_value); + return ::setsockopt(fd, level, option_name, option_value_p, sizeof(option_value)); +} + +bool +ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec) +{ + switch (m_fd_recv_type) + { + case eFDTypeFile: // Other FD requireing read/write + break; + + case eFDTypeSocket: // Socket requiring send/recv + case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom + { + // Check in case timeout for m_fd has already been set to this value + if (timeout_usec == m_socket_timeout_usec) + return true; + //printf ("ConnectionFileDescriptor::SetSocketReceiveTimeout (timeout_usec = %u)\n", timeout_usec); + + struct timeval timeout; + if (timeout_usec == UINT32_MAX) + { + timeout.tv_sec = 0; + timeout.tv_usec = 0; + } + else if (timeout_usec == 0) + { + // Sending in zero does an infinite timeout, so set this as low + // as we can go to get an effective zero timeout... + timeout.tv_sec = 0; + timeout.tv_usec = 1; + } + else + { + timeout.tv_sec = timeout_usec / TimeValue::MicroSecPerSec; + timeout.tv_usec = timeout_usec % TimeValue::MicroSecPerSec; + } + if (::setsockopt (m_fd_recv, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == 0) + { + m_socket_timeout_usec = timeout_usec; + return true; + } + } + } + return false; +} + +in_port_t +ConnectionFileDescriptor::GetSocketPort (int fd) +{ + // We bound to port zero, so we need to figure out which port we actually bound to + SocketAddress sock_addr; + socklen_t sock_addr_len = sock_addr.GetMaxLength (); + if (::getsockname (fd, sock_addr, &sock_addr_len) == 0) + return sock_addr.GetPort (); + + return 0; +} + +// If the read file descriptor is a socket, then return +// the port number that is being used by the socket. +in_port_t +ConnectionFileDescriptor::GetReadPort () const +{ + return ConnectionFileDescriptor::GetSocketPort (m_fd_recv); +} + +// If the write file descriptor is a socket, then return +// the port number that is being used by the socket. +in_port_t +ConnectionFileDescriptor::GetWritePort () const +{ + return ConnectionFileDescriptor::GetSocketPort (m_fd_send); +} + + diff --git a/source/Core/ConnectionMachPort.cpp b/source/Core/ConnectionMachPort.cpp new file mode 100644 index 00000000000..ca818d405a2 --- /dev/null +++ b/source/Core/ConnectionMachPort.cpp @@ -0,0 +1,323 @@ +//===-- ConnectionMachPort.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#if defined(__APPLE__) + +#include "lldb/Core/ConnectionMachPort.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +struct MessageType +{ + mach_msg_header_t head; + ConnectionMachPort::PayloadType payload; +}; + + + +ConnectionMachPort::ConnectionMachPort () : + Connection(), + m_task(mach_task_self()), + m_port(MACH_PORT_TYPE_NONE) +{ +} + +ConnectionMachPort::~ConnectionMachPort () +{ + Disconnect (NULL); +} + +bool +ConnectionMachPort::IsConnected () const +{ + return m_port != MACH_PORT_TYPE_NONE; +} + +ConnectionStatus +ConnectionMachPort::Connect (const char *s, Error *error_ptr) +{ + if (IsConnected()) + { + if (error_ptr) + error_ptr->SetErrorString ("already connected"); + return eConnectionStatusError; + } + + if (s == NULL || s[0] == '\0') + { + if (error_ptr) + error_ptr->SetErrorString ("empty connect URL"); + return eConnectionStatusError; + } + + ConnectionStatus status = eConnectionStatusError; + + if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://"))) + { + s += strlen("bootstrap-checkin://"); + + if (*s) + { + status = BootstrapCheckIn (s, error_ptr); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("bootstrap port name is empty"); + } + } + else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://"))) + { + s += strlen("bootstrap-lookup://"); + if (*s) + { + status = BootstrapLookup (s, error_ptr); + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("bootstrap port name is empty"); + } + } + else + { + if (error_ptr) + error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); + } + + + if (status == eConnectionStatusSuccess) + { + if (error_ptr) + error_ptr->Clear(); + } + else + { + Disconnect(NULL); + } + + return status; +} + +ConnectionStatus +ConnectionMachPort::BootstrapCheckIn (const char *port, Error *error_ptr) +{ + mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; + + /* Getting bootstrap server port */ + kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + if (kret == KERN_SUCCESS) + { + name_t port_name; + int len = snprintf(port_name, sizeof(port_name), "%s", port); + if (len < sizeof(port_name)) + { + kret = ::bootstrap_check_in (bootstrap_port, + port_name, + &m_port); + } + else + { + Disconnect(NULL); + if (error_ptr) + error_ptr->SetErrorString ("bootstrap is too long"); + return eConnectionStatusError; + } + } + + if (kret != KERN_SUCCESS) + { + Disconnect(NULL); + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + return eConnectionStatusError; + } + return eConnectionStatusSuccess; +} + +lldb::ConnectionStatus +ConnectionMachPort::BootstrapLookup (const char *port, + Error *error_ptr) +{ + name_t port_name; + + if (port && port[0]) + { + if (::snprintf (port_name, sizeof (port_name), "%s", port) >= sizeof (port_name)) + { + if (error_ptr) + error_ptr->SetErrorString ("port netname is too long"); + return eConnectionStatusError; + } + } + else + { + if (error_ptr) + error_ptr->SetErrorString ("empty port netname"); + return eConnectionStatusError; + } + + mach_port_t bootstrap_port = MACH_PORT_TYPE_NONE; + + /* Getting bootstrap server port */ + kern_return_t kret = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + if (kret == KERN_SUCCESS) + { + kret = ::bootstrap_look_up (bootstrap_port, + port_name, + &m_port); + } + + if (kret != KERN_SUCCESS) + { + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + return eConnectionStatusError; + } + + return eConnectionStatusSuccess; +} + +ConnectionStatus +ConnectionMachPort::Disconnect (Error *error_ptr) +{ + kern_return_t kret; + + // TODO: verify if we need to netname_check_out for + // either or both + if (m_port != MACH_PORT_TYPE_NONE) + { + kret = ::mach_port_deallocate (m_task, m_port); + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + m_port = MACH_PORT_TYPE_NONE; + } + + return eConnectionStatusSuccess; +} + +size_t +ConnectionMachPort::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + PayloadType payload; + + kern_return_t kret = Receive (payload); + if (kret == KERN_SUCCESS) + { + memcpy (dst, payload.data, payload.data_length); + status = eConnectionStatusSuccess; + return payload.data_length; + } + + if (error_ptr) + error_ptr->SetError (kret, eErrorTypeMachKernel); + status = eConnectionStatusError; + return 0; +} + +size_t +ConnectionMachPort::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + PayloadType payload; + payload.command = 0; + payload.data_length = src_len; + const size_t max_payload_size = sizeof(payload.data); + if (src_len > max_payload_size) + payload.data_length = max_payload_size; + memcpy (payload.data, src, payload.data_length); + + if (Send (payload) == KERN_SUCCESS) + { + status = eConnectionStatusSuccess; + return payload.data_length; + } + status = eConnectionStatusError; + return 0; +} + +ConnectionStatus +ConnectionMachPort::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + return eConnectionStatusLostConnection; +} + +kern_return_t +ConnectionMachPort::Send (const PayloadType &payload) +{ + struct MessageType message; + + /* (i) Form the message : */ + + /* (i.a) Fill the header fields : */ + message.head.msgh_bits = MACH_MSGH_BITS_REMOTE (MACH_MSG_TYPE_MAKE_SEND) | + MACH_MSGH_BITS_OTHER (MACH_MSGH_BITS_COMPLEX); + message.head.msgh_size = sizeof(MessageType); + message.head.msgh_local_port = MACH_PORT_NULL; + message.head.msgh_remote_port = m_port; + + /* (i.b) Explain the message type ( an integer ) */ + // message.type.msgt_name = MACH_MSG_TYPE_INTEGER_32; + // message.type.msgt_size = 32; + // message.type.msgt_number = 1; + // message.type.msgt_inline = TRUE; + // message.type.msgt_longform = FALSE; + // message.type.msgt_deallocate = FALSE; + /* message.type.msgt_unused = 0; */ /* not needed, I think */ + + /* (i.c) Fill the message with the given integer : */ + message.payload = payload; + + /* (ii) Send the message : */ + kern_return_t kret = ::mach_msg (&message.head, + MACH_SEND_MSG, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + return kret; +} + +kern_return_t +ConnectionMachPort::Receive (PayloadType &payload) +{ + MessageType message; + message.head.msgh_size = sizeof(MessageType); + + kern_return_t kret = ::mach_msg (&message.head, + MACH_RCV_MSG, + 0, + sizeof(MessageType), + m_port, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + + if (kret == KERN_SUCCESS) + payload = message.payload; + + return kret; +} + + +#endif // #if defined(__APPLE__) diff --git a/source/Core/ConnectionSharedMemory.cpp b/source/Core/ConnectionSharedMemory.cpp new file mode 100644 index 00000000000..625f17a0985 --- /dev/null +++ b/source/Core/ConnectionSharedMemory.cpp @@ -0,0 +1,131 @@ +//===-- ConnectionSharedMemory.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ConnectionSharedMemory.h" + +// C Includes +#include +#include +#include +#include +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Communication.h" +#include "lldb/Core/Log.h" + +using namespace lldb; +using namespace lldb_private; + +ConnectionSharedMemory::ConnectionSharedMemory () : + Connection(), + m_name(), + m_fd (-1), + m_mmap() +{ +} + +ConnectionSharedMemory::~ConnectionSharedMemory () +{ + Disconnect (NULL); +} + +bool +ConnectionSharedMemory::IsConnected () const +{ + return m_fd >= 0; +} + +ConnectionStatus +ConnectionSharedMemory::Connect (const char *s, Error *error_ptr) +{ +// if (s && s[0]) +// { +// if (strstr(s, "shm-create://")) +// { +// } +// else if (strstr(s, "shm-connect://")) +// { +// } +// if (error_ptr) +// error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); +// return eConnectionStatusError; +// } + if (error_ptr) + error_ptr->SetErrorString("invalid connect arguments"); + return eConnectionStatusError; +} + +ConnectionStatus +ConnectionSharedMemory::Disconnect (Error *error_ptr) +{ + m_mmap.Clear(); + if (!m_name.empty()) + { + shm_unlink (m_name.c_str()); + m_name.clear(); + } + return eConnectionStatusSuccess; +} + +size_t +ConnectionSharedMemory::Read (void *dst, + size_t dst_len, + uint32_t timeout_usec, + ConnectionStatus &status, + Error *error_ptr) +{ + status = eConnectionStatusSuccess; + return 0; +} + +size_t +ConnectionSharedMemory::Write (const void *src, size_t src_len, ConnectionStatus &status, Error *error_ptr) +{ + status = eConnectionStatusSuccess; + return 0; +} + +ConnectionStatus +ConnectionSharedMemory::BytesAvailable (uint32_t timeout_usec, Error *error_ptr) +{ + return eConnectionStatusLostConnection; +} + +ConnectionStatus +ConnectionSharedMemory::Open (bool create, const char *name, size_t size, Error *error_ptr) +{ + if (m_fd != -1) + { + if (error_ptr) + error_ptr->SetErrorString("already open"); + return eConnectionStatusError; + } + + m_name.assign (name); + int oflag = O_RDWR; + if (create) + oflag |= O_CREAT; + m_fd = ::shm_open (m_name.c_str(), oflag, S_IRUSR|S_IWUSR); + + if (create) + ::ftruncate (m_fd, size); + + if (m_mmap.MemoryMapFromFileDescriptor(m_fd, 0, size, true, false) == size) + return eConnectionStatusSuccess; + + Disconnect(NULL); + return eConnectionStatusError; +} + diff --git a/source/Core/ConstString.cpp b/source/Core/ConstString.cpp new file mode 100644 index 00000000000..875169428d2 --- /dev/null +++ b/source/Core/ConstString.cpp @@ -0,0 +1,342 @@ +//===-- ConstString.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" +#include "llvm/ADT/StringMap.h" + +using namespace lldb_private; + + +class Pool +{ +public: + typedef const char * StringPoolValueType; + typedef llvm::StringMap StringPool; + typedef llvm::StringMapEntry StringPoolEntryType; + + //------------------------------------------------------------------ + // Default constructor + // + // Initialize the member variables and create the empty string. + //------------------------------------------------------------------ + Pool () : + m_mutex (Mutex::eMutexTypeRecursive), + m_string_map () + { + } + + //------------------------------------------------------------------ + // Destructor + //------------------------------------------------------------------ + ~Pool () + { + } + + + static StringPoolEntryType & + GetStringMapEntryFromKeyData (const char *keyData) + { + char *ptr = const_cast(keyData) - sizeof (StringPoolEntryType); + return *reinterpret_cast(ptr); + } + + size_t + GetConstCStringLength (const char *ccstr) const + { + if (ccstr) + { + const StringPoolEntryType&entry = GetStringMapEntryFromKeyData (ccstr); + return entry.getKey().size(); + } + return 0; + } + + StringPoolValueType + GetMangledCounterpart (const char *ccstr) const + { + if (ccstr) + return GetStringMapEntryFromKeyData (ccstr).getValue(); + return 0; + } + + bool + SetMangledCounterparts (const char *key_ccstr, const char *value_ccstr) + { + if (key_ccstr && value_ccstr) + { + GetStringMapEntryFromKeyData (key_ccstr).setValue(value_ccstr); + GetStringMapEntryFromKeyData (value_ccstr).setValue(key_ccstr); + return true; + } + return false; + } + + const char * + GetConstCString (const char *cstr) + { + if (cstr) + return GetConstCStringWithLength (cstr, strlen (cstr)); + return NULL; + } + + const char * + GetConstCStringWithLength (const char *cstr, size_t cstr_len) + { + if (cstr) + { + Mutex::Locker locker (m_mutex); + llvm::StringRef string_ref (cstr, cstr_len); + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + return entry.getKeyData(); + } + return NULL; + } + + const char * + GetConstCStringWithStringRef (const llvm::StringRef &string_ref) + { + if (string_ref.data()) + { + Mutex::Locker locker (m_mutex); + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (string_ref, (StringPoolValueType)NULL); + return entry.getKeyData(); + } + return NULL; + } + + const char * + GetConstCStringAndSetMangledCounterPart (const char *demangled_cstr, const char *mangled_ccstr) + { + if (demangled_cstr) + { + Mutex::Locker locker (m_mutex); + // Make string pool entry with the mangled counterpart already set + StringPoolEntryType& entry = m_string_map.GetOrCreateValue (llvm::StringRef (demangled_cstr), mangled_ccstr); + + // Extract the const version of the demangled_cstr + const char *demangled_ccstr = entry.getKeyData(); + // Now assign the demangled const string as the counterpart of the + // mangled const string... + GetStringMapEntryFromKeyData (mangled_ccstr).setValue(demangled_ccstr); + // Return the constant demangled C string + return demangled_ccstr; + } + return NULL; + } + + const char * + GetConstTrimmedCStringWithLength (const char *cstr, size_t cstr_len) + { + if (cstr) + { + const size_t trimmed_len = std::min (strlen (cstr), cstr_len); + return GetConstCStringWithLength (cstr, trimmed_len); + } + return NULL; + } + + //------------------------------------------------------------------ + // 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 + { + Mutex::Locker locker (m_mutex); + size_t mem_size = sizeof(Pool); + const_iterator end = m_string_map.end(); + for (const_iterator pos = m_string_map.begin(); pos != end; ++pos) + { + mem_size += sizeof(StringPoolEntryType) + pos->getKey().size(); + } + return mem_size; + } + +protected: + //------------------------------------------------------------------ + // Typedefs + //------------------------------------------------------------------ + typedef StringPool::iterator iterator; + typedef StringPool::const_iterator const_iterator; + + //------------------------------------------------------------------ + // Member variables + //------------------------------------------------------------------ + mutable Mutex m_mutex; + StringPool m_string_map; +}; + +//---------------------------------------------------------------------- +// 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. +// +// FIXME: If we are going to keep it this way we should come up with some +// abstraction to "pthread_once" so we don't have to check the pointer +// every time. +//---------------------------------------------------------------------- +static Pool & +StringPool() +{ + static Mutex g_pool_initialization_mutex; + static Pool *g_string_pool = NULL; + + if (g_string_pool == NULL) + { + Mutex::Locker initialization_locker(g_pool_initialization_mutex); + if (g_string_pool == NULL) + { + 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().GetConstCStringWithLength (s.data(), s.size())) +{ +} + +bool +ConstString::operator < (const ConstString& rhs) const +{ + if (m_string == rhs.m_string) + return false; + + llvm::StringRef lhs_string_ref (m_string, StringPool().GetConstCStringLength (m_string)); + llvm::StringRef rhs_string_ref (rhs.m_string, StringPool().GetConstCStringLength (rhs.m_string)); + + // 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 NULL, so if LHS is NULL then it is less than + return lhs_string_ref.data() == NULL; +} + +Stream& +lldb_private::operator << (Stream& s, const ConstString& str) +{ + const char *cstr = str.GetCString(); + if (cstr) + s << cstr; + + return s; +} + +size_t +ConstString::GetLength () const +{ + return StringPool().GetConstCStringLength (m_string); +} + +int +ConstString::Compare (const ConstString& lhs, const ConstString& rhs) +{ + // If the iterators are the same, this is the same string + register const char *lhs_cstr = lhs.m_string; + register 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_cstr, StringPool().GetConstCStringLength (lhs_cstr)); + llvm::StringRef rhs_string_ref (rhs_cstr, StringPool().GetConstCStringLength (rhs_cstr)); + return lhs_string_ref.compare(rhs_string_ref); + } + + if (lhs_cstr) + return +1; // LHS isn't NULL but RHS is + else + return -1; // LHS is NULL but RHS isn't +} + +void +ConstString::Dump(Stream *s, const char *fail_value) const +{ + if (s) + { + const char *cstr = AsCString (fail_value); + if (cstr) + 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-NULL string + const char *parens = cstr ? "\"" : ""; + s->Printf("%*p: ConstString, string = %s%s%s, length = %" PRIu64, (int)sizeof(void*) * 2, this, parens, cstr, parens, (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::SetCStringWithMangledCounterpart (const char *demangled, const ConstString &mangled) +{ + m_string = StringPool().GetConstCStringAndSetMangledCounterPart (demangled, mangled.m_string); +} + +bool +ConstString::GetMangledCounterpart (ConstString &counterpart) const +{ + counterpart.m_string = StringPool().GetMangledCounterpart(m_string); + return 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(); +} diff --git a/source/Core/DataBufferHeap.cpp b/source/Core/DataBufferHeap.cpp new file mode 100644 index 00000000000..2c8a865b966 --- /dev/null +++ b/source/Core/DataBufferHeap.cpp @@ -0,0 +1,111 @@ +//===-- DataBufferHeap.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/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 () +{ +} + +//---------------------------------------------------------------------- +// Return a pointer to the bytes owned by this object, or NULL if +// the object contains no bytes. +//---------------------------------------------------------------------- +uint8_t * +DataBufferHeap::GetBytes () +{ + if (m_data.empty()) + return NULL; + return &m_data[0]; +} + +//---------------------------------------------------------------------- +// Return a const pointer to the bytes owned by this object, or NULL +// if the object contains no bytes. +//---------------------------------------------------------------------- +const uint8_t * +DataBufferHeap::GetBytes () const +{ + if (m_data.empty()) + return NULL; + return &m_data[0]; +} + +//---------------------------------------------------------------------- +// 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 = (const uint8_t *)src; + if (src && src_len > 0) + m_data.assign (src_u8, src_u8 + src_len); + else + m_data.clear(); +} + +void +DataBufferHeap::Clear() +{ + buffer_t empty; + m_data.swap(empty); +} diff --git a/source/Core/DataBufferMemoryMap.cpp b/source/Core/DataBufferMemoryMap.cpp new file mode 100644 index 00000000000..a4382a0c67e --- /dev/null +++ b/source/Core/DataBufferMemoryMap.cpp @@ -0,0 +1,258 @@ +//===-- DataBufferMemoryMap.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include +#include +#include +#include +#include + +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/Log.h" +#include "lldb/lldb-private-log.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default Constructor +//---------------------------------------------------------------------- +DataBufferMemoryMap::DataBufferMemoryMap() : + m_mmap_addr(NULL), + m_mmap_size(0), + m_data(NULL), + m_size(0) +{ +} + +//---------------------------------------------------------------------- +// Virtual destructor since this class inherits from a pure virtual +// base class. +//---------------------------------------------------------------------- +DataBufferMemoryMap::~DataBufferMemoryMap() +{ + Clear(); +} + +//---------------------------------------------------------------------- +// Return a pointer to the bytes owned by this object, or NULL if +// the object contains no bytes. +//---------------------------------------------------------------------- +uint8_t * +DataBufferMemoryMap::GetBytes() +{ + return m_data; +} + +//---------------------------------------------------------------------- +// Return a const pointer to the bytes owned by this object, or NULL +// if the object contains no bytes. +//---------------------------------------------------------------------- +const uint8_t * +DataBufferMemoryMap::GetBytes() const +{ + return m_data; +} + +//---------------------------------------------------------------------- +// Return the number of bytes this object currently contains. +//---------------------------------------------------------------------- +uint64_t +DataBufferMemoryMap::GetByteSize() const +{ + return m_size; +} + +//---------------------------------------------------------------------- +// Reverts this object to an empty state by unmapping any memory +// that is currently owned. +//---------------------------------------------------------------------- +void +DataBufferMemoryMap::Clear() +{ + if (m_mmap_addr != NULL) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); + if (log) + log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size); + ::munmap((void *)m_mmap_addr, m_mmap_size); + m_mmap_addr = NULL; + m_mmap_size = 0; + m_data = NULL; + m_size = 0; + } +} + +//---------------------------------------------------------------------- +// Memory map "length" bytes from "file" starting "offset" +// bytes into the file. If "length" is set to SIZE_MAX, then +// map as many bytes as possible. +// +// Returns the number of bytes mapped starting from the requested +// offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec, + lldb::offset_t offset, + lldb::offset_t length, + bool writeable) +{ + if (filespec != NULL) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(file=\"%s\", offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i", + filespec->GetPath().c_str(), + offset, + length, + writeable); + } + char path[PATH_MAX]; + if (filespec->GetPath(path, sizeof(path))) + { + uint32_t options = File::eOpenOptionRead; + if (writeable) + options |= File::eOpenOptionWrite; + + File file; + Error error (file.Open(path, options)); + if (error.Success()) + { + const bool fd_is_file = true; + return MemoryMapFromFileDescriptor (file.GetDescriptor(), offset, length, writeable, fd_is_file); + } + } + } + // We should only get here if there was an error + Clear(); + return 0; +} + + +//---------------------------------------------------------------------- +// The file descriptor FD is assumed to already be opened as read only +// and the STAT structure is assumed to a valid pointer and already +// containing valid data from a call to stat(). +// +// Memory map FILE_LENGTH bytes in FILE starting FILE_OFFSET bytes into +// the file. If FILE_LENGTH is set to SIZE_MAX, then map as many bytes +// as possible. +// +// RETURNS +// Number of bytes mapped starting from the requested offset. +//---------------------------------------------------------------------- +size_t +DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, + lldb::offset_t offset, + lldb::offset_t length, + bool writeable, + bool fd_is_file) +{ + Clear(); + if (fd >= 0) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE)); + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", + fd, + offset, + length, + writeable, + fd_is_file); + } + struct stat stat; + if (::fstat(fd, &stat) == 0) + { + if (S_ISREG(stat.st_mode) && (stat.st_size > offset)) + { + const size_t max_bytes_available = stat.st_size - offset; + if (length == SIZE_MAX) + { + length = max_bytes_available; + } + else if (length > max_bytes_available) + { + // Cap the length if too much data was requested + length = max_bytes_available; + } + + if (length > 0) + { + int prot = PROT_READ; + if (writeable) + prot |= PROT_WRITE; + + int flags = MAP_PRIVATE; + if (fd_is_file) + flags |= MAP_FILE; + + m_mmap_addr = (uint8_t *)::mmap(NULL, length, prot, flags, fd, offset); + Error error; + + if (m_mmap_addr == (void*)-1) + { + error.SetErrorToErrno (); + if (error.GetError() == EINVAL) + { + // We may still have a shot at memory mapping if we align things correctly + size_t page_offset = offset % Host::GetPageSize(); + if (page_offset != 0) + { + m_mmap_addr = (uint8_t *)::mmap(NULL, length + page_offset, prot, flags, fd, offset - page_offset); + if (m_mmap_addr == (void*)-1) + { + // Failed to map file + m_mmap_addr = NULL; + } + else if (m_mmap_addr != NULL) + { + // We recovered and were able to memory map + // after we aligned things to page boundaries + + // Save the actual mmap'ed size + m_mmap_size = length + page_offset; + // Our data is at an offset into the the mapped data + m_data = m_mmap_addr + page_offset; + // Our pretend size is the size that was requestd + m_size = length; + } + } + } + if (error.GetError() == ENOMEM) + { + error.SetErrorStringWithFormat("could not allocate %" PRId64 " bytes of memory to mmap in file", (uint64_t) length); + } + } + else + { + // We were able to map the requested data in one chunk + // where our mmap and actual data are the same. + m_mmap_size = length; + m_data = m_mmap_addr; + m_size = length; + } + + if (log) + { + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %zu, error = %s", + m_mmap_addr, m_mmap_size, error.AsCString()); + } + } + } + } + } + return GetByteSize (); +} diff --git a/source/Core/DataEncoder.cpp b/source/Core/DataEncoder.cpp new file mode 100644 index 00000000000..92a9104acc3 --- /dev/null +++ b/source/Core/DataEncoder.cpp @@ -0,0 +1,335 @@ +//===-- DataEncoder.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataEncoder.h" + +#include +#include + +#include "llvm/Support/MathExtras.h" + +#include "lldb/Core/DataBuffer.h" +#include "lldb/Host/Endian.h" + +using namespace lldb; +using namespace lldb_private; + +static inline void +WriteInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +{ + *(uint16_t *)(ptr + offset) = value; +} +static inline void +WriteInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +{ + *(uint32_t *)(ptr + offset) = value; +} + +static inline void +WriteInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +{ + *(uint64_t *)(ptr + offset) = value; +} + +static inline void +WriteSwappedInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +{ + *(uint16_t *)(ptr + offset) = llvm::ByteSwap_16(value); +} + +static inline void +WriteSwappedInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +{ + *(uint32_t *)(ptr + offset) = llvm::ByteSwap_32(value); +} + +static inline void +WriteSwappedInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +{ + *(uint64_t *)(ptr + offset) = llvm::ByteSwap_64(value); +} + +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataEncoder::DataEncoder () : + m_start (NULL), + m_end (NULL), + m_byte_order(lldb::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 ((uint8_t*)data), + m_end ((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 (NULL), + m_end (NULL), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ + SetData (data_sp); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DataEncoder::~DataEncoder () +{ +} + +//------------------------------------------------------------------ +// 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 = NULL; + m_end = NULL; + m_byte_order = lldb::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 != NULL) + { + const DataBuffer * data = m_data_sp.get(); + if (data != NULL) + { + const uint8_t * data_bytes = data->GetBytes(); + if (data_bytes != NULL) + { + 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 (const void *bytes, uint32_t length, ByteOrder endian) +{ + m_byte_order = endian; + m_data_sp.reset(); + if (bytes == NULL || length == 0) + { + m_start = NULL; + m_end = NULL; + } + else + { + m_start = (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 = NULL; + + if (data_length > 0) + { + m_data_sp = data_sp; + if (data_sp.get()) + { + 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 != lldb::endian::InlHostByteOrder()) + WriteSwappedInt16 (m_start, offset, value); + else + WriteInt16 (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 != lldb::endian::InlHostByteOrder()) + WriteSwappedInt32 (m_start, offset, value); + else + WriteInt32 (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 != lldb::endian::InlHostByteOrder()) + WriteSwappedInt64 (m_start, offset, value); + else + WriteInt64 (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: + assert(!"GetMax64 unhandled case!"); + break; + } + return UINT32_MAX; +} + +uint32_t +DataEncoder::PutData (uint32_t offset, const void *src, uint32_t src_len) +{ + if (src == NULL || 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) + return PutData (offset, cstr, strlen(cstr) + 1); + return UINT32_MAX; +} diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp new file mode 100644 index 00000000000..518faeb71ea --- /dev/null +++ b/source/Core/DataExtractor.cpp @@ -0,0 +1,2179 @@ +//===-- DataExtractor.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include +#include + +#include +#include +#include +#include + +#include "clang/AST/ASTContext.h" + +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/MathExtras.h" + + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/UUID.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ExecutionContextScope.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +static inline uint16_t +ReadInt16(const unsigned char* ptr, offset_t offset) +{ + return *(uint16_t *)(ptr + offset); +} +static inline uint32_t +ReadInt32 (const unsigned char* ptr, offset_t offset) +{ + return *(uint32_t *)(ptr + offset); +} + +static inline uint64_t +ReadInt64(const unsigned char* ptr, offset_t offset) +{ + return *(uint64_t *)(ptr + offset); +} + +static inline uint16_t +ReadInt16(const void* ptr) +{ + return *(uint16_t *)(ptr); +} +static inline uint32_t +ReadInt32 (const void* ptr) +{ + return *(uint32_t *)(ptr); +} + +static inline uint64_t +ReadInt64(const void* ptr) +{ + return *(uint64_t *)(ptr); +} + +static inline uint16_t +ReadSwapInt16(const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_16(*(uint16_t *)(ptr + offset)); +} + +static inline uint32_t +ReadSwapInt32 (const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_32(*(uint32_t *)(ptr + offset)); +} +static inline uint64_t +ReadSwapInt64(const unsigned char* ptr, offset_t offset) +{ + return llvm::ByteSwap_64(*(uint64_t *)(ptr + offset)); +} + +static inline uint16_t +ReadSwapInt16(const void* ptr) +{ + return llvm::ByteSwap_16(*(uint16_t *)(ptr)); +} + +static inline uint32_t +ReadSwapInt32 (const void* ptr) +{ + return llvm::ByteSwap_32(*(uint32_t *)(ptr)); +} +static inline uint64_t +ReadSwapInt64(const void* ptr) +{ + return llvm::ByteSwap_64(*(uint64_t *)(ptr)); +} + +#define NON_PRINTABLE_CHAR '.' +//---------------------------------------------------------------------- +// Default constructor. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor () : + m_start (NULL), + m_end (NULL), + m_byte_order(lldb::endian::InlHostByteOrder()), + m_addr_size (4), + 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. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const void* data, offset_t length, ByteOrder endian, uint32_t addr_size) : + m_start ((uint8_t*)data), + m_end ((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 DataExtractor objects exist that have a reference to +// this data. +//---------------------------------------------------------------------- +DataExtractor::DataExtractor (const DataBufferSP& data_sp, ByteOrder endian, uint32_t addr_size) : + m_start (NULL), + m_end (NULL), + m_byte_order(endian), + m_addr_size (addr_size), + m_data_sp () +{ + 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) : + m_start(NULL), + m_end(NULL), + m_byte_order(data.m_byte_order), + m_addr_size(data.m_addr_size), + m_data_sp() +{ + 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) +{ +} + +//---------------------------------------------------------------------- +// 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; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DataExtractor::~DataExtractor () +{ +} + +//------------------------------------------------------------------ +// 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 = NULL; + m_end = NULL; + m_byte_order = lldb::endian::InlHostByteOrder(); + m_addr_size = 4; + 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 != NULL) + { + const DataBuffer * data = m_data_sp.get(); + if (data != NULL) + { + const uint8_t * data_bytes = data->GetBytes(); + if (data_bytes != NULL) + { + 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 == NULL || length == 0) + { + m_start = NULL; + m_end = NULL; + } + else + { + m_start = (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; + // If "data" contains shared pointer to data, then we can use that + if (data.m_data_sp.get()) + { + 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 = NULL; + + if (data_length > 0) + { + m_data_sp = data_sp; + if (data_sp.get()) + { + 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 = (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-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available in +// the buffer due to being out of bounds, or unsufficient data. +//---------------------------------------------------------------------- +void * +DataExtractor::GetU8 (offset_t *offset_ptr, void *dst, uint32_t count) const +{ + const uint8_t *data = (const uint8_t *)GetData (offset_ptr, count); + if (data) + { + // Copy the data into the buffer + memcpy (dst, data, count); + // Return a non-NULL pointer to the converted data as an indicator of success + return dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// 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 = (const uint8_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::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 == lldb::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 == lldb::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 == lldb::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-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available +// in the buffer due to being out of bounds, or unsufficient 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 = (const uint16_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint16_t *dst_pos = (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-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// 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 uint32_t *data = (const uint32_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt32 (data); + else + val = *data; + } + 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-NULL buffer pointer upon successful extraction of +// all the requested bytes, or NULL when the data is not available +// in the buffer due to being out of bounds, or unsufficient 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 = (const uint32_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint32_t *dst_pos = (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-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// 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 uint64_t *data = (const uint64_t *)GetData (offset_ptr, sizeof(val)); + if (data) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + val = ReadSwapInt64 (data); + else + val = *data; + } + 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 = (const uint64_t *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + uint64_t *dst_pos = (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-NULL pointer to the converted data as an indicator of success + return void_dst; + } + return NULL; +} + +//---------------------------------------------------------------------- +// 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 between 1 and 4 since the return value is only 32 bits +// wide. Any "byte_size" values less than 1 or greater than 4 will +// result in nothing being extracted, and zero being returned. +// +// RETURNS the integer value that was extracted, or zero on failure. +//---------------------------------------------------------------------- +uint32_t +DataExtractor::GetMaxU32 (offset_t *offset_ptr, size_t byte_size) const +{ + switch (byte_size) + { + case 1: return GetU8 (offset_ptr); break; + case 2: return GetU16(offset_ptr); break; + case 4: return GetU32(offset_ptr); break; + default: + assert("GetMaxU32 unhandled case!" == NULL); + break; + } + return 0; +} + +//---------------------------------------------------------------------- +// 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. +//---------------------------------------------------------------------- +uint64_t +DataExtractor::GetMaxU64 (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return GetU8 (offset_ptr); break; + case 2: return GetU16(offset_ptr); break; + case 4: return GetU32(offset_ptr); break; + case 8: return GetU64(offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +uint64_t +DataExtractor::GetMaxU64_unchecked (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return GetU8_unchecked (offset_ptr); break; + case 2: return GetU16_unchecked (offset_ptr); break; + case 4: return GetU32_unchecked (offset_ptr); break; + case 8: return GetU64_unchecked (offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +int64_t +DataExtractor::GetMaxS64 (offset_t *offset_ptr, size_t size) const +{ + switch (size) + { + case 1: return (int8_t)GetU8 (offset_ptr); break; + case 2: return (int16_t)GetU16(offset_ptr); break; + case 4: return (int32_t)GetU32(offset_ptr); break; + case 8: return (int64_t)GetU64(offset_ptr); break; + default: + assert("GetMax64 unhandled case!" == NULL); + break; + } + return 0; +} + +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) + { + if (bitfield_bit_offset > 0) + uval64 >>= bitfield_bit_offset; + 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) + { + if (bitfield_bit_offset > 0) + sval64 >>= bitfield_bit_offset; + uint64_t bitfield_mask = (((uint64_t)1) << bitfield_bit_size) - 1; + sval64 &= bitfield_mask; + // sign extend if needed + if (sval64 & (((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 = (const float_type *)GetData (offset_ptr, src_size); + if (src) + { + if (m_byte_order != lldb::endian::InlHostByteOrder()) + { + const uint8_t *src_data = (const uint8_t *)src; + uint8_t *dst_data = (uint8_t *)&val; + for (size_t i=0; im_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 +{ + return GetMaxU64 (offset_ptr, m_addr_size); +} + +uint64_t +DataExtractor::GetAddress_unchecked (offset_t *offset_ptr) const +{ + 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 +{ + return GetMaxU64 (offset_ptr, m_addr_size); +} + +//---------------------------------------------------------------------- +// GetDwarfEHPtr +// +// Used for calls when the value type is specified by a DWARF EH Frame +// pointer encoding. +//---------------------------------------------------------------------- + +uint64_t +DataExtractor::GetGNUEHPointer (offset_t *offset_ptr, uint32_t eh_ptr_enc, lldb::addr_t pc_rel_addr, lldb::addr_t text_addr, lldb::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 = GetAddressByteSize(); + + 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 = GetAddress (offset_ptr); +// if (data_relocs) +// addressValue = data_relocs->Relocate(*offset_ptr - addr_size, *this, addressValue); + } + break; + case DW_EH_PE_uleb128 : addressValue = GetULEB128(offset_ptr); break; + case DW_EH_PE_udata2 : addressValue = GetU16(offset_ptr); break; + case DW_EH_PE_udata4 : addressValue = GetU32(offset_ptr); break; + case DW_EH_PE_udata8 : addressValue = GetU64(offset_ptr); break; + case DW_EH_PE_sleb128 : addressValue = GetSLEB128(offset_ptr); break; + case DW_EH_PE_sdata2 : addressValue = (int16_t)GetU16(offset_ptr); break; + case DW_EH_PE_sdata4 : addressValue = (int32_t)GetU32(offset_ptr); break; + case DW_EH_PE_sdata8 : addressValue = (int64_t)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; +} + +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()) + { + for (uint32_t i=0; i 0); + assert (m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle); + + // Validate the destination info + assert (dst_void_ptr != NULL); + assert (dst_len > 0); + assert (dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle); + + // 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; + + uint32_t i; + uint8_t* dst = (uint8_t*)dst_void_ptr; + const uint8_t* src = (const uint8_t *)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 (i=0; i 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 (i=0; i= 0x80) + { + result &= 0x7f; + int shift = 7; + while (src < end) + { + uint8_t byte = *src++; + result |= (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 = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + 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 |= (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 = (const uint8_t *)PeekData (*offset_ptr, 1); + if (src == NULL) + 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; +} + +static bool +GetAPInt (const DataExtractor &data, lldb::offset_t *offset_ptr, lldb::offset_t byte_size, llvm::APInt &result) +{ + llvm::SmallVector 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_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_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; +} + +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 * 0x1.0p125f; + } + + v <<= 13; + u.u = v | 0x70000000U; + return u.f * 0x1.0p-112f; +} + +lldb::offset_t +DataExtractor::Dump (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) const +{ + if (s == NULL) + 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(), NULL, NULL)); + 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, *this, 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); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disassembler_sp->GetInstructionList().Clear(); + } + } + } + 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; 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((num_per_line - (offset - line_start_offset)) * 3 + 2), ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, LLDB_INVALID_OFFSET, 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))); + line_start_offset = offset; + } + else + if (item_format != eFormatChar && + item_format != eFormatCharPrintable && + item_format != eFormatCharArray && + count > 0) + { + s->PutChar(' '); + } + + uint32_t i; + switch (item_format) + { + case eFormatBoolean: + if (item_byte_size <= 8) + s->Printf ("%s", GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset) ? "true" : "false"); + else + { + s->Printf("error: unsupported byte size (%zu) for boolean format", item_byte_size); + return offset; + } + break; + + case eFormatBinary: + if (item_byte_size <= 8) + { + uint64_t uval64 = 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 (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, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatBytes: + case eFormatBytesWithASCII: + for (i=0; iPrintf ("%2.2x", 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: + { + // 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 = 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, GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = true; + const unsigned radix = 10; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatUnsigned: + if (item_byte_size <= 8) + s->Printf ("%" PRIu64, GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = false; + const unsigned radix = 10; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatOctal: + if (item_byte_size <= 8) + s->Printf ("0%" PRIo64, GetMaxS64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + else + { + const bool is_signed = false; + const unsigned radix = 8; + offset = DumpAPInt (s, *this, offset, item_byte_size, is_signed, radix); + } + break; + + case eFormatOSType: + { + uint64_t uval64 = GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset); + s->PutChar('\''); + for (i=0; i> ((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 = 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(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 <= 8) + { + s->Printf("%" PRIu64, GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); + s->Printf(" + %" PRIu64 "i", GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); + } + else + { + s->Printf("error: unsupported byte size (%zu) for complex integer format", item_byte_size); + return offset; + } + } + break; + + case eFormatComplex: + if (sizeof(float) * 2 == item_byte_size) + { + float f32_1 = GetFloat (&offset); + float f32_2 = GetFloat (&offset); + + s->Printf ("%g + %gi", f32_1, f32_2); + break; + } + else if (sizeof(double) * 2 == item_byte_size) + { + double d64_1 = GetDouble (&offset); + double d64_2 = GetDouble (&offset); + + s->Printf ("%lg + %lgi", d64_1, d64_2); + break; + } + else if (sizeof(long double) * 2 == item_byte_size) + { + long double ld64_1 = GetLongDouble (&offset); + long double ld64_2 = GetLongDouble (&offset); + s->Printf ("%Lg + %Lgi", ld64_1, ld64_2); + break; + } + else + { + s->Printf("error: unsupported byte size (%zu) for complex float format", item_byte_size); + return offset; + } + break; + + default: + case eFormatDefault: + case eFormatHex: + case eFormatHexUppercase: + { + bool wantsuppercase = (item_format == eFormatHexUppercase); + if (item_byte_size <= 8) + { + s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64, (int)(2 * item_byte_size), (int)(2 * item_byte_size), GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); + } + else + { + assert (item_bit_size == 0 && item_bit_offset == 0); + s->PutCString("0x"); + const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); + if (bytes) + { + uint32_t idx; + if (m_byte_order == 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; + + case eFormatFloat: + { + TargetSP target_sp; + bool used_apfloat = 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 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, this->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 (*this, &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)) + { + llvm::APInt apint; + switch (target_sp->GetArchitecture().GetCore()) + { + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_64_x86_64: + // clang will assert when contructing the apfloat if we use a 16 byte integer value + if (GetAPInt (*this, &offset, 10, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->LongDoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + break; + + default: + if (GetAPInt (*this, &offset, item_byte_size, apint)) + { + llvm::APFloat apfloat (ast->getFloatTypeSemantics(ast->LongDoubleTy), apint); + apfloat.toString(sv, format_precision, format_max_padding); + } + break; + } + } + else if (item_bit_size == ast->getTypeSize(ast->HalfTy)) + { + llvm::APInt apint(item_bit_size, this->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_apfloat = true; + } + } + } + } + + if (!used_apfloat) + { + std::ostringstream ss; + if (item_byte_size == sizeof(float) || item_byte_size == 2) + { + float f; + if (item_byte_size == 2) + { + uint16_t half = this->GetU16(&offset); + f = half2float(half); + } + else + { + f = GetFloat (&offset); + } + ss.precision(std::numeric_limits::digits10); + ss << f; + } + else if (item_byte_size == sizeof(double)) + { + ss.precision(std::numeric_limits::digits10); + ss << GetDouble(&offset); + } + else if (item_byte_size == sizeof(long double) || item_byte_size == 10) + { + ss.precision(std::numeric_limits::digits10); + ss << GetLongDouble(&offset); + } + else + { + s->Printf("error: unsupported byte size (%zu) for float format", item_byte_size); + return offset; + } + ss.flush(); + s->Printf("%s", ss.str().c_str()); + } + } + break; + + case eFormatUnicode16: + s->Printf("U+%4.4x", GetU16 (&offset)); + break; + + case eFormatUnicode32: + s->Printf("U+0x%8.8x", GetU32 (&offset)); + break; + + case eFormatAddressInfo: + { + addr_t addr = 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 (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 (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 (%zu) for hex float format", 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 = Dump (s, offset, eFormatCharArray, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt8: + s->PutChar('{'); + offset = Dump (s, offset, eFormatDecimal, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfUInt8: + s->PutChar('{'); + offset = Dump (s, offset, eFormatHex, 1, item_byte_size, item_byte_size, LLDB_INVALID_ADDRESS, 0, 0); + s->PutChar('}'); + break; + + case eFormatVectorOfSInt16: + s->PutChar('{'); + offset = Dump (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 = Dump (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 = Dump (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 = Dump (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 = Dump (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 = Dump (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 eFormatVectorOfFloat32: + s->PutChar('{'); + offset = Dump (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 = Dump (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 = Dump (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((num_per_line - (offset - line_start_offset)) * 3 + 2), ""); + Dump(s, line_start_offset, eFormatCharPrintable, 1, offset - line_start_offset, LLDB_INVALID_OFFSET, LLDB_INVALID_ADDRESS, 0, 0); + } + return offset; // Return the offset at which we ended up +} + +//---------------------------------------------------------------------- +// 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 NULL, then an appropriate format +// string will be used for the supplied "type". If the stream "s" +// is NULL, 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 == NULL) + 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->Printf("%s", sstr.GetData()); + 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 ":", (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.GetSize() > 0) + log->Printf("%s", sstr.GetData()); + + return offset; // Return the offset at which we ended up +} + +//---------------------------------------------------------------------- +// DumpUUID +// +// Dump out a UUID starting at 'offset' bytes into the buffer +//---------------------------------------------------------------------- +void +DataExtractor::DumpUUID (Stream *s, offset_t offset) const +{ + if (s) + { + const uint8_t *uuid_data = PeekData(offset, 16); + if ( uuid_data ) + { + lldb_private::UUID uuid(uuid_data, 16); + uuid.Dump(s); + } + else + { + s->Printf("", offset); + } + } +} + +void +DataExtractor::DumpHexBytes (Stream *s, + const void *src, + size_t src_len, + uint32_t bytes_per_line, + addr_t base_addr) +{ + DataExtractor data (src, src_len, eByteOrderLittle, 4); + data.Dump (s, + 0, // Offset into "src" + 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 +} + +size_t +DataExtractor::Copy (DataExtractor &dest_data) const +{ + if (m_data_sp.get()) + { + // 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 = NULL; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (buffer_sp.get() == NULL || buffer_heap_ptr == NULL) + 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 == NULL) + return false; + + if (length == 0) + return true; + + size_t bytes = GetByteSize() + length; + + DataBufferHeap *buffer_heap_ptr = NULL; + DataBufferSP buffer_sp(buffer_heap_ptr = new DataBufferHeap(bytes, 0)); + + if (buffer_sp.get() == NULL || buffer_heap_ptr == NULL) + 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; +} diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp new file mode 100644 index 00000000000..d1d2ebb0550 --- /dev/null +++ b/source/Core/Debugger.cpp @@ -0,0 +1,2695 @@ +//===-- Debugger.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/API/SBDebugger.h" + +#include "lldb/Core/Debugger.h" + +#include + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" + +#include "lldb/lldb-private.h" +#include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/State.h" +#include "lldb/Core/StreamAsynchronousIO.h" +#include "lldb/Core/StreamCallback.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Host/DynamicLibrary.h" +#include "lldb/Host/Terminal.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueSInt64.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/TargetList.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/AnsiTerminal.h" + +using namespace lldb; +using namespace lldb_private; + + +static uint32_t g_shared_debugger_refcount = 0; +static lldb::user_id_t g_unique_id = 1; + +#pragma mark Static Functions + +static Mutex & +GetDebuggerListMutex () +{ + static Mutex g_mutex(Mutex::eMutexTypeRecursive); + return g_mutex; +} + +typedef std::vector DebuggerList; + +static DebuggerList & +GetDebuggerList() +{ + // hide the static debugger list inside a singleton accessor to avoid + // global init contructors + static DebuggerList g_list; + return g_list; +} + +OptionEnumValueElement +g_show_disassembly_enum_values[] = +{ + { Debugger::eStopDisassemblyTypeNever, "never", "Never show disassembly when displaying a stop context."}, + { 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."}, + { 0, NULL, NULL } +}; + +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."}, + { 0, NULL, NULL } +}; + +#define MODULE_WITH_FUNC "{ ${module.file.basename}{`${function.name-with-args}${function.pc-offset}}}" +#define FILE_AND_LINE "{ at ${line.file.basename}:${line.number}}" + +#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id%tid}"\ + "{, ${frame.pc}}"\ + MODULE_WITH_FUNC\ + FILE_AND_LINE\ + "{, name = '${thread.name}'}"\ + "{, queue = '${thread.queue}'}"\ + "{, stop reason = ${thread.stop-reason}}"\ + "{\\nReturn value: ${thread.return-value}}"\ + "\\n" + +#define DEFAULT_FRAME_FORMAT "frame #${frame.index}: ${frame.pc}"\ + MODULE_WITH_FUNC\ + FILE_AND_LINE\ + "\\n" + + + +static PropertyDefinition +g_properties[] = +{ +{ "auto-confirm", OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true all confirmation prompts will receive their default reply." }, +{ "frame-format", OptionValue::eTypeString , true, 0 , DEFAULT_FRAME_FORMAT, NULL, "The default frame format string to use when displaying stack frame information for threads." }, +{ "notify-void", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Notify the user explicitly if an expression returns void (default: false)." }, +{ "prompt", OptionValue::eTypeString , true, OptionValueString::eOptionEncodeCharacterEscapeSequences, "(lldb) ", NULL, "The debugger command line prompt displayed for the user." }, +{ "script-lang", OptionValue::eTypeEnum , true, eScriptLanguagePython, NULL, g_language_enumerators, "The script language to be used for evaluating user-written scripts." }, +{ "stop-disassembly-count", OptionValue::eTypeSInt64 , true, 4 , NULL, NULL, "The number of disassembly lines to show when displaying a stopped context." }, +{ "stop-disassembly-display", OptionValue::eTypeEnum , true, Debugger::eStopDisassemblyTypeNoSource, NULL, g_show_disassembly_enum_values, "Control when to display disassembly when displaying a stopped context." }, +{ "stop-line-count-after", OptionValue::eTypeSInt64 , true, 3 , NULL, NULL, "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 , NULL, NULL, "The number of sources lines to display that come before the current source line when displaying a stopped context." }, +{ "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL, NULL, "The maximum number of columns to use for displaying text." }, +{ "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, +{ "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, +{ "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, + + { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } +}; + +enum +{ + ePropertyAutoConfirm = 0, + ePropertyFrameFormat, + ePropertyNotiftVoid, + ePropertyPrompt, + ePropertyScriptLanguage, + ePropertyStopDisassemblyCount, + ePropertyStopDisassemblyDisplay, + ePropertyStopLineCountAfter, + ePropertyStopLineCountBefore, + ePropertyTerminalWidth, + ePropertyThreadFormat, + ePropertyUseExternalEditor, + ePropertyUseColor, +}; + +// +//const char * +//Debugger::GetFrameFormat() const +//{ +// return m_properties_sp->GetFrameFormat(); +//} +//const char * +//Debugger::GetThreadFormat() const +//{ +// return m_properties_sp->GetThreadFormat(); +//} +// + + +Error +Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *property_path, + const char *value) +{ + bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0; + 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(); + } + Error 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 (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0) + { + const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); + EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); + GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); + } + else if (strcmp(property_path, g_properties[ePropertyUseColor].name) == 0) + { + // 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 errors; + StreamString feedback_stream; + if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) + { + for (auto error : errors) + { + GetErrorStream().Printf("%s\n",error.AsCString()); + } + if (feedback_stream.GetSize()) + GetErrorStream().Printf("%s",feedback_stream.GetData()); + } + } + } + } + return error; +} + +bool +Debugger::GetAutoConfirm () const +{ + const uint32_t idx = ePropertyAutoConfirm; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +const char * +Debugger::GetFrameFormat() const +{ + const uint32_t idx = ePropertyFrameFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +bool +Debugger::GetNotifyVoid () const +{ + const uint32_t idx = ePropertyNotiftVoid; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +const char * +Debugger::GetPrompt() const +{ + const uint32_t idx = ePropertyPrompt; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +void +Debugger::SetPrompt(const char *p) +{ + const uint32_t idx = ePropertyPrompt; + m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p); + const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); + EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));; + GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); +} + +const char * +Debugger::GetThreadFormat() const +{ + const uint32_t idx = ePropertyThreadFormat; + return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, g_properties[idx].default_cstr_value); +} + +lldb::ScriptLanguage +Debugger::GetScriptLanguage() const +{ + const uint32_t idx = ePropertyScriptLanguage; + return (lldb::ScriptLanguage)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); +} + +bool +Debugger::SetScriptLanguage (lldb::ScriptLanguage script_lang) +{ + const uint32_t idx = ePropertyScriptLanguage; + return m_collection_sp->SetPropertyAtIndexAsEnumeration (NULL, idx, script_lang); +} + +uint32_t +Debugger::GetTerminalWidth () const +{ + const uint32_t idx = ePropertyTerminalWidth; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +bool +Debugger::SetTerminalWidth (uint32_t term_width) +{ + const uint32_t idx = ePropertyTerminalWidth; + return m_collection_sp->SetPropertyAtIndexAsSInt64 (NULL, idx, term_width); +} + +bool +Debugger::GetUseExternalEditor () const +{ + const uint32_t idx = ePropertyUseExternalEditor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseExternalEditor (bool b) +{ + const uint32_t idx = ePropertyUseExternalEditor; + return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); +} + +bool +Debugger::GetUseColor () const +{ + const uint32_t idx = ePropertyUseColor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseColor (bool b) +{ + const uint32_t idx = ePropertyUseColor; + bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); + SetPrompt (GetPrompt()); + return ret; +} + +uint32_t +Debugger::GetStopSourceLineCount (bool before) const +{ + const uint32_t idx = before ? ePropertyStopLineCountBefore : ePropertyStopLineCountAfter; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +Debugger::StopDisassemblyType +Debugger::GetStopDisassemblyDisplay () const +{ + const uint32_t idx = ePropertyStopDisassemblyDisplay; + return (Debugger::StopDisassemblyType)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value); +} + +uint32_t +Debugger::GetDisassemblyLineCount () const +{ + const uint32_t idx = ePropertyStopDisassemblyCount; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + +#pragma mark Debugger + +//const DebuggerPropertiesSP & +//Debugger::GetSettings() const +//{ +// return m_properties_sp; +//} +// + +int +Debugger::TestDebuggerRefCount () +{ + return g_shared_debugger_refcount; +} + +void +Debugger::Initialize () +{ + if (g_shared_debugger_refcount++ == 0) + lldb_private::Initialize(); +} + +void +Debugger::Terminate () +{ + if (g_shared_debugger_refcount > 0) + { + g_shared_debugger_refcount--; + if (g_shared_debugger_refcount == 0) + { + lldb_private::WillTerminate(); + lldb_private::Terminate(); + + // Clear our master list of debugger objects + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().clear(); + } + } +} + +void +Debugger::SettingsInitialize () +{ + Target::SettingsInitialize (); +} + +void +Debugger::SettingsTerminate () +{ + Target::SettingsTerminate (); +} + +bool +Debugger::LoadPlugin (const FileSpec& spec, Error& error) +{ + lldb::DynamicLibrarySP dynlib_sp(new lldb_private::DynamicLibrary(spec)); + if (!dynlib_sp || dynlib_sp->IsValid() == false) + { + if (spec.Exists()) + error.SetErrorString("this file does not represent a loadable dylib"); + else + error.SetErrorString("no such file"); + return false; + } + lldb::DebuggerSP debugger_sp(shared_from_this()); + 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 = dynlib_sp->GetSymbol("_ZN4lldb16PluginInitializeENS_10SBDebuggerE"); + if (!init_func) + { + error.SetErrorString("cannot find the initialization function lldb::PluginInitialize(lldb::SBDebugger)"); + return false; + } + if (init_func(debugger_sb)) + { + m_loaded_plugins.push_back(dynlib_sp); + return true; + } + error.SetErrorString("dylib refused to be loaded"); + return false; +} + +static FileSpec::EnumerateDirectoryResult +LoadPluginCallback +( + void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec + ) +{ + Error error; + + static ConstString g_dylibext("dylib"); + static ConstString g_solibext("so"); + + if (!baton) + return FileSpec::eEnumerateDirectoryResultQuit; + + Debugger *debugger = (Debugger*)baton; + + // 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 (file_type == FileSpec::eFileTypeRegular || + file_type == FileSpec::eFileTypeSymbolicLink || + file_type == FileSpec::eFileTypeUnknown ) + { + FileSpec plugin_file_spec (file_spec); + plugin_file_spec.ResolvePath (); + + if (plugin_file_spec.GetFileNameExtension() != g_dylibext && + plugin_file_spec.GetFileNameExtension() != g_solibext) + { + return FileSpec::eEnumerateDirectoryResultNext; + } + + Error plugin_load_error; + debugger->LoadPlugin (plugin_file_spec, plugin_load_error); + + return FileSpec::eEnumerateDirectoryResultNext; + } + + else if (file_type == FileSpec::eFileTypeUnknown || + file_type == FileSpec::eFileTypeDirectory || + file_type == FileSpec::eFileTypeSymbolicLink ) + { + // 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 enurating a file system that doesn't have correct file type + // information. + return FileSpec::eEnumerateDirectoryResultEnter; + } + + return FileSpec::eEnumerateDirectoryResultNext; +} + +void +Debugger::InstanceInitialize () +{ + FileSpec dir_spec; + const bool find_directories = true; + const bool find_files = true; + const bool find_other = true; + char dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypeLLDBSystemPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + this); + } + } + + if (Host::GetLLDBPath (ePathTypeLLDBUserPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::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_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().push_back(debugger_sp); + } + debugger_sp->InstanceInitialize (); + return debugger_sp; +} + +void +Debugger::Destroy (DebuggerSP &debugger_sp) +{ + if (debugger_sp.get() == NULL) + return; + + debugger_sp->Clear(); + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList (); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin (); pos != end; ++pos) + { + if ((*pos).get() == debugger_sp.get()) + { + debugger_list.erase (pos); + return; + } + } + } +} + +DebuggerSP +Debugger::FindDebuggerWithInstanceName (const ConstString &instance_name) +{ + DebuggerSP debugger_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + + for (pos = debugger_list.begin(); pos != end; ++pos) + { + if ((*pos).get()->m_instance_name == instance_name) + { + debugger_sp = *pos; + break; + } + } + } + return debugger_sp; +} + +TargetSP +Debugger::FindTargetWithProcessID (lldb::pid_t pid) +{ + TargetSP target_sp; + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.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_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.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(OptionValuePropertiesSP(new OptionValueProperties())), + m_input_comm("debugger.input"), + m_input_file (), + m_output_file (), + m_error_file (), + m_terminal_state (), + m_target_list (*this), + m_platform_list (), + m_listener ("lldb.Debugger"), + m_source_manager_ap(), + m_source_file_cache(), + m_command_interpreter_ap (new CommandInterpreter (*this, eScriptLanguageDefault, false)), + m_input_reader_stack (), + m_input_reader_data (), + m_instance_name() +{ + 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.reset (new StreamCallback (log_callback, baton)); + m_command_interpreter_ap->Initialize (); + // Always add our default platform to the platform list + PlatformSP default_platform_sp (Platform::GetDefaultPlatform()); + assert (default_platform_sp.get()); + 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()); + if (m_command_interpreter_ap.get()) + { + m_collection_sp->AppendProperty (ConstString("interpreter"), + ConstString("Settings specify to the debugger's command interpreter."), + true, + m_command_interpreter_ap->GetValueProperties()); + } + OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64 (NULL, 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); +} + +Debugger::~Debugger () +{ + Clear(); +} + +void +Debugger::Clear() +{ + CleanUpInputReaders(); + m_listener.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(); + } + } + BroadcasterManager::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(); + GetInputFile().Close (); + // Now that we have closed m_input_file, we can now tell our input communication + // class to close down. Its read thread should quickly exit after we close + // the input file handle above. + m_input_comm.Clear (); +} + +bool +Debugger::GetCloseInputOnEOF () const +{ + return m_input_comm.GetCloseOnEOF(); +} + +void +Debugger::SetCloseInputOnEOF (bool b) +{ + m_input_comm.SetCloseOnEOF(b); +} + +bool +Debugger::GetAsyncExecution () +{ + return !m_command_interpreter_ap->GetSynchronous(); +} + +void +Debugger::SetAsyncExecution (bool async_execution) +{ + m_command_interpreter_ap->SetSynchronous (!async_execution); +} + + +void +Debugger::SetInputFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &in_file = GetInputFile(); + in_file.SetStream (fh, tranfer_ownership); + if (in_file.IsValid() == false) + in_file.SetStream (stdin, true); + + // Disconnect from any old connection if we had one + m_input_comm.Disconnect (); + // Pass false as the second argument to ConnectionFileDescriptor below because + // our "in_file" above will already take ownership if requested and we don't + // want to objects trying to own and close a file descriptor. + m_input_comm.SetConnection (new ConnectionFileDescriptor (in_file.GetDescriptor(), false)); + m_input_comm.SetReadThreadBytesReceivedCallback (Debugger::DispatchInputCallback, this); + + // Save away the terminal state if that is relevant, so that we can restore it in RestoreInputState. + SaveInputTerminalState (); + + Error error; + if (m_input_comm.StartReadThread (&error) == false) + { + File &err_file = GetErrorFile(); + + err_file.Printf ("error: failed to main input read thread: %s", error.AsCString() ? error.AsCString() : "unkown error"); + exit(1); + } +} + +void +Debugger::SetOutputFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &out_file = GetOutputFile(); + out_file.SetStream (fh, tranfer_ownership); + if (out_file.IsValid() == false) + 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 + const bool can_create = false; + ScriptInterpreter* script_interpreter = GetCommandInterpreter().GetScriptInterpreter(can_create); + if (script_interpreter) + script_interpreter->ResetOutputFileHandle (fh); +} + +void +Debugger::SetErrorFileHandle (FILE *fh, bool tranfer_ownership) +{ + File &err_file = GetErrorFile(); + err_file.SetStream (fh, tranfer_ownership); + if (err_file.IsValid() == false) + err_file.SetStream (stderr, false); +} + +void +Debugger::SaveInputTerminalState () +{ + File &in_file = GetInputFile(); + 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() == false) + { + 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() == NULL) + exe_ctx.SetFrameSP (thread_sp->GetStackFrameAtIndex (0)); + } + } + } + return exe_ctx; + +} + +InputReaderSP +Debugger::GetCurrentInputReader () +{ + InputReaderSP reader_sp; + + if (!m_input_reader_stack.IsEmpty()) + { + // Clear any finished readers from the stack + while (CheckIfTopInputReaderIsDone()) ; + + if (!m_input_reader_stack.IsEmpty()) + reader_sp = m_input_reader_stack.Top(); + } + + return reader_sp; +} + +void +Debugger::DispatchInputCallback (void *baton, const void *bytes, size_t bytes_len) +{ + if (bytes_len > 0) + ((Debugger *)baton)->DispatchInput ((char *)bytes, bytes_len); + else + ((Debugger *)baton)->DispatchInputEndOfFile (); +} + + +void +Debugger::DispatchInput (const char *bytes, size_t bytes_len) +{ + if (bytes == NULL || bytes_len == 0) + return; + + WriteToDefaultReader (bytes, bytes_len); +} + +void +Debugger::DispatchInputInterrupt () +{ + m_input_reader_data.clear(); + + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderInterrupt); + + // If notifying the reader of the interrupt finished the reader, we should pop it off the stack. + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::DispatchInputEndOfFile () +{ + m_input_reader_data.clear(); + + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderEndOfFile); + + // If notifying the reader of the end-of-file finished the reader, we should pop it off the stack. + while (CheckIfTopInputReaderIsDone ()) ; + } +} + +void +Debugger::CleanUpInputReaders () +{ + m_input_reader_data.clear(); + + // The bottom input reader should be the main debugger input reader. We do not want to close that one here. + while (m_input_reader_stack.GetSize() > 1) + { + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (reader_sp) + { + reader_sp->Notify (eInputReaderEndOfFile); + reader_sp->SetIsDone (true); + } + } +} + +void +Debugger::NotifyTopInputReader (InputReaderAction notification) +{ + InputReaderSP reader_sp (GetCurrentInputReader()); + if (reader_sp) + { + reader_sp->Notify (notification); + + // Flush out any input readers that are done. + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + } +} + +bool +Debugger::InputReaderIsTopReader (const InputReaderSP& reader_sp) +{ + InputReaderSP top_reader_sp (GetCurrentInputReader()); + + return (reader_sp.get() == top_reader_sp.get()); +} + + +void +Debugger::WriteToDefaultReader (const char *bytes, size_t bytes_len) +{ + if (bytes && bytes_len) + m_input_reader_data.append (bytes, bytes_len); + + if (m_input_reader_data.empty()) + return; + + while (!m_input_reader_stack.IsEmpty() && !m_input_reader_data.empty()) + { + // Get the input reader from the top of the stack + InputReaderSP reader_sp (GetCurrentInputReader ()); + if (!reader_sp) + break; + + size_t bytes_handled = reader_sp->HandleRawBytes (m_input_reader_data.c_str(), + m_input_reader_data.size()); + if (bytes_handled) + { + m_input_reader_data.erase (0, bytes_handled); + } + else + { + // No bytes were handled, we might not have reached our + // granularity, just return and wait for more data + break; + } + } + + // Flush out any input readers that are done. + while (CheckIfTopInputReaderIsDone ()) + /* Do nothing. */; + +} + +void +Debugger::PushInputReader (const InputReaderSP& reader_sp) +{ + if (!reader_sp) + return; + + // Deactivate the old top reader + InputReaderSP top_reader_sp (GetCurrentInputReader ()); + + if (top_reader_sp) + top_reader_sp->Notify (eInputReaderDeactivate); + + m_input_reader_stack.Push (reader_sp); + reader_sp->Notify (eInputReaderActivate); + ActivateInputReader (reader_sp); +} + +bool +Debugger::PopInputReader (const InputReaderSP& pop_reader_sp) +{ + bool result = false; + + // The reader on the stop of the stack is done, so let the next + // read on the stack referesh its prompt and if there is one... + if (!m_input_reader_stack.IsEmpty()) + { + // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. + InputReaderSP reader_sp(m_input_reader_stack.Top()); + + if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) + { + m_input_reader_stack.Pop (); + reader_sp->Notify (eInputReaderDeactivate); + reader_sp->Notify (eInputReaderDone); + result = true; + + if (!m_input_reader_stack.IsEmpty()) + { + reader_sp = m_input_reader_stack.Top(); + if (reader_sp) + { + ActivateInputReader (reader_sp); + reader_sp->Notify (eInputReaderReactivate); + } + } + } + } + return result; +} + +bool +Debugger::CheckIfTopInputReaderIsDone () +{ + bool result = false; + if (!m_input_reader_stack.IsEmpty()) + { + // Cannot call GetCurrentInputReader here, as that would cause an infinite loop. + InputReaderSP reader_sp(m_input_reader_stack.Top()); + + if (reader_sp && reader_sp->IsDone()) + { + result = true; + PopInputReader (reader_sp); + } + } + return result; +} + +void +Debugger::ActivateInputReader (const InputReaderSP &reader_sp) +{ + int input_fd = m_input_file.GetFile().GetDescriptor(); + + if (input_fd >= 0) + { + Terminal tty(input_fd); + + tty.SetEcho(reader_sp->GetEcho()); + + switch (reader_sp->GetGranularity()) + { + case eInputReaderGranularityByte: + case eInputReaderGranularityWord: + tty.SetCanonical (false); + break; + + case eInputReaderGranularityLine: + case eInputReaderGranularityAll: + tty.SetCanonical (true); + break; + + default: + break; + } + } +} + +StreamSP +Debugger::GetAsyncOutputStream () +{ + return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), + CommandInterpreter::eBroadcastBitAsynchronousOutputData)); +} + +StreamSP +Debugger::GetAsyncErrorStream () +{ + return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), + CommandInterpreter::eBroadcastBitAsynchronousErrorData)); +} + +size_t +Debugger::GetNumDebuggers() +{ + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + return GetDebuggerList().size(); + } + return 0; +} + +lldb::DebuggerSP +Debugger::GetDebuggerAtIndex (size_t index) +{ + DebuggerSP debugger_sp; + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + + if (index < debugger_list.size()) + debugger_sp = debugger_list[index]; + } + + return debugger_sp; +} + +DebuggerSP +Debugger::FindDebuggerWithID (lldb::user_id_t id) +{ + DebuggerSP debugger_sp; + + if (g_shared_debugger_refcount > 0) + { + Mutex::Locker locker (GetDebuggerListMutex ()); + DebuggerList &debugger_list = GetDebuggerList(); + DebuggerList::iterator pos, end = debugger_list.end(); + for (pos = debugger_list.begin(); pos != end; ++pos) + { + if ((*pos).get()->GetID() == id) + { + debugger_sp = *pos; + break; + } + } + } + return debugger_sp; +} + +static void +TestPromptFormats (StackFrame *frame) +{ + if (frame == NULL) + return; + + StreamString s; + const char *prompt_format = + "{addr = '${addr}'\n}" + "{process.id = '${process.id}'\n}" + "{process.name = '${process.name}'\n}" + "{process.file.basename = '${process.file.basename}'\n}" + "{process.file.fullpath = '${process.file.fullpath}'\n}" + "{thread.id = '${thread.id}'\n}" + "{thread.index = '${thread.index}'\n}" + "{thread.name = '${thread.name}'\n}" + "{thread.queue = '${thread.queue}'\n}" + "{thread.stop-reason = '${thread.stop-reason}'\n}" + "{target.arch = '${target.arch}'\n}" + "{module.file.basename = '${module.file.basename}'\n}" + "{module.file.fullpath = '${module.file.fullpath}'\n}" + "{file.basename = '${file.basename}'\n}" + "{file.fullpath = '${file.fullpath}'\n}" + "{frame.index = '${frame.index}'\n}" + "{frame.pc = '${frame.pc}'\n}" + "{frame.sp = '${frame.sp}'\n}" + "{frame.fp = '${frame.fp}'\n}" + "{frame.flags = '${frame.flags}'\n}" + "{frame.reg.rdi = '${frame.reg.rdi}'\n}" + "{frame.reg.rip = '${frame.reg.rip}'\n}" + "{frame.reg.rsp = '${frame.reg.rsp}'\n}" + "{frame.reg.rbp = '${frame.reg.rbp}'\n}" + "{frame.reg.rflags = '${frame.reg.rflags}'\n}" + "{frame.reg.xmm0 = '${frame.reg.xmm0}'\n}" + "{frame.reg.carp = '${frame.reg.carp}'\n}" + "{function.id = '${function.id}'\n}" + "{function.name = '${function.name}'\n}" + "{function.name-with-args = '${function.name-with-args}'\n}" + "{function.addr-offset = '${function.addr-offset}'\n}" + "{function.line-offset = '${function.line-offset}'\n}" + "{function.pc-offset = '${function.pc-offset}'\n}" + "{line.file.basename = '${line.file.basename}'\n}" + "{line.file.fullpath = '${line.file.fullpath}'\n}" + "{line.number = '${line.number}'\n}" + "{line.start-addr = '${line.start-addr}'\n}" + "{line.end-addr = '${line.end-addr}'\n}" +; + + SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything)); + ExecutionContext exe_ctx; + frame->CalculateExecutionContext(exe_ctx); + if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s)) + { + printf("%s\n", s.GetData()); + } + else + { + printf ("what we got: %s\n", s.GetData()); + } +} + +static bool +ScanFormatDescriptor (const char* var_name_begin, + const char* var_name_end, + const char** var_name_final, + const char** percent_position, + Format* custom_format, + ValueObject::ValueObjectRepresentationStyle* val_obj_display) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + *percent_position = ::strchr(var_name_begin,'%'); + if (!*percent_position || *percent_position > var_name_end) + { + if (log) + log->Printf("[ScanFormatDescriptor] no format descriptor in string, skipping"); + *var_name_final = var_name_end; + } + else + { + *var_name_final = *percent_position; + std::string format_name(*var_name_final+1, var_name_end-*var_name_final-1); + if (log) + log->Printf("[ScanFormatDescriptor] parsing %s as a format descriptor", format_name.c_str()); + if ( !FormatManager::GetFormatFromCString(format_name.c_str(), + true, + *custom_format) ) + { + if (log) + log->Printf("[ScanFormatDescriptor] %s is an unknown format", format_name.c_str()); + + switch (format_name.front()) + { + case '@': // if this is an @ sign, print ObjC description + *val_obj_display = ValueObject::eValueObjectRepresentationStyleLanguageSpecific; + break; + case 'V': // if this is a V, print the value using the default format + *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + break; + case 'L': // if this is an L, print the location of the value + *val_obj_display = ValueObject::eValueObjectRepresentationStyleLocation; + break; + case 'S': // if this is an S, print the summary after all + *val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; + break; + case '#': // if this is a '#', print the number of children + *val_obj_display = ValueObject::eValueObjectRepresentationStyleChildrenCount; + break; + case 'T': // if this is a 'T', print the type + *val_obj_display = ValueObject::eValueObjectRepresentationStyleType; + break; + case 'N': // if this is a 'N', print the name + *val_obj_display = ValueObject::eValueObjectRepresentationStyleName; + break; + case '>': // if this is a '>', print the name + *val_obj_display = ValueObject::eValueObjectRepresentationStyleExpressionPath; + break; + default: + if (log) + log->Printf("ScanFormatDescriptor] %s is an error, leaving the previous value alone", format_name.c_str()); + break; + } + } + // a good custom format tells us to print the value using it + else + { + if (log) + log->Printf("[ScanFormatDescriptor] will display value for this VO"); + *val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + } + if (log) + log->Printf("[ScanFormatDescriptor] final format description outcome: custom_format = %d, val_obj_display = %d", + *custom_format, + *val_obj_display); + return true; +} + +static bool +ScanBracketedRange (const char* var_name_begin, + const char* var_name_end, + const char* var_name_final, + const char** open_bracket_position, + const char** separator_position, + const char** close_bracket_position, + const char** var_name_final_if_array_range, + int64_t* index_lower, + int64_t* index_higher) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + *open_bracket_position = ::strchr(var_name_begin,'['); + if (*open_bracket_position && *open_bracket_position < var_name_final) + { + *separator_position = ::strchr(*open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield + *close_bracket_position = ::strchr(*open_bracket_position,']'); + // as usual, we assume that [] will come before % + //printf("trying to expand a []\n"); + *var_name_final_if_array_range = *open_bracket_position; + if (*close_bracket_position - *open_bracket_position == 1) + { + if (log) + log->Printf("[ScanBracketedRange] '[]' detected.. going from 0 to end of data"); + *index_lower = 0; + } + else if (*separator_position == NULL || *separator_position > var_name_end) + { + char *end = NULL; + *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); + *index_higher = *index_lower; + if (log) + log->Printf("[ScanBracketedRange] [%" PRId64 "] detected, high index is same", *index_lower); + } + else if (*close_bracket_position && *close_bracket_position < var_name_end) + { + char *end = NULL; + *index_lower = ::strtoul (*open_bracket_position+1, &end, 0); + *index_higher = ::strtoul (*separator_position+1, &end, 0); + if (log) + log->Printf("[ScanBracketedRange] [%" PRId64 "-%" PRId64 "] detected", *index_lower, *index_higher); + } + else + { + if (log) + log->Printf("[ScanBracketedRange] expression is erroneous, cannot extract indices out of it"); + return false; + } + if (*index_lower > *index_higher && *index_higher > 0) + { + if (log) + log->Printf("[ScanBracketedRange] swapping indices"); + int64_t temp = *index_lower; + *index_lower = *index_higher; + *index_higher = temp; + } + } + else if (log) + log->Printf("[ScanBracketedRange] no bracketed range, skipping entirely"); + return true; +} + +template +static bool RunScriptFormatKeyword(Stream &s, ScriptInterpreter *script_interpreter, T t, const std::string& script_name) +{ + if (script_interpreter) + { + Error script_error; + std::string script_output; + + if (script_interpreter->RunScriptFormatKeyword(script_name.c_str(), t, script_output, script_error) && script_error.Success()) + { + s.Printf("%s", script_output.c_str()); + return true; + } + else + { + s.Printf("",script_error.AsCString()); + } + } + return false; +} + +static ValueObjectSP +ExpandIndexedExpression (ValueObject* valobj, + size_t index, + StackFrame* frame, + bool deref_pointer) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + 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()); + const char* first_unparsed; + 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(), + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &what_next); + if (!item) + { + if (log) + log->Printf("[ExpandIndexedExpression] ERROR: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + else + { + if (log) + log->Printf("[ExpandIndexedExpression] ALL RIGHT: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + return item; +} + +static inline bool +IsToken(const char *var_name_begin, const char *var) +{ + return (::strncmp (var_name_begin, var, strlen(var)) == 0); +} + +static bool +IsTokenWithFormat(const char *var_name_begin, const char *var, std::string &format, const char *default_format, + const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr) +{ + int var_len = strlen(var); + if (::strncmp (var_name_begin, var, var_len) == 0) + { + var_name_begin += var_len; + if (*var_name_begin == '}') + { + format = default_format; + return true; + } + else if (*var_name_begin == '%') + { + // Allow format specifiers: x|X|u with optional width specifiers. + // ${thread.id%x} ; hex + // ${thread.id%X} ; uppercase hex + // ${thread.id%u} ; unsigned decimal + // ${thread.id%8.8X} ; width.precision + specifier + // ${thread.id%tid} ; unsigned on FreeBSD/Linux, otherwise default_format (0x%4.4x for thread.id) + int dot_count = 0; + const char *specifier = NULL; + int width_precision_length = 0; + const char *width_precision = ++var_name_begin; + while (isdigit(*var_name_begin) || *var_name_begin == '.') + { + dot_count += (*var_name_begin == '.'); + if (dot_count > 1) + break; + var_name_begin++; + width_precision_length++; + } + + if (IsToken (var_name_begin, "tid}")) + { + Target *target = Target::GetTargetFromContexts (exe_ctx_ptr, sc_ptr); + if (target) + { + 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)) + specifier = PRIu64; + } + if (!specifier) + { + format = default_format; + return true; + } + } + else if (IsToken (var_name_begin, "x}")) + specifier = PRIx64; + else if (IsToken (var_name_begin, "X}")) + specifier = PRIX64; + else if (IsToken (var_name_begin, "u}")) + specifier = PRIu64; + + if (specifier) + { + format = "%"; + if (width_precision_length) + format += std::string(width_precision, width_precision_length); + format += specifier; + return true; + } + } + } + return false; +} + +static bool +FormatPromptRecurse +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + const char **end, + ValueObject* valobj +) +{ + ValueObject* realvalobj = NULL; // makes it super-easy to parse pointers + bool success = true; + const char *p; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (p = format; *p != '\0'; ++p) + { + if (realvalobj) + { + valobj = realvalobj; + realvalobj = NULL; + } + size_t non_special_chars = ::strcspn (p, "${}\\"); + if (non_special_chars > 0) + { + if (success) + s.Write (p, non_special_chars); + p += non_special_chars; + } + + if (*p == '\0') + { + break; + } + else if (*p == '{') + { + // Start a new scope that must have everything it needs if it is to + // to make it into the final output stream "s". If you want to make + // a format that only prints out the function or symbol name if there + // is one in the symbol context you can use: + // "{function =${function.name}}" + // The first '{' starts a new scope that end with the matching '}' at + // the end of the string. The contents "function =${function.name}" + // will then be evaluated and only be output if there is a function + // or symbol with a valid name. + StreamString sub_strm; + + ++p; // Skip the '{' + + if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) + { + // The stream had all it needed + s.Write(sub_strm.GetData(), sub_strm.GetSize()); + } + if (*p != '}') + { + success = false; + break; + } + } + else if (*p == '}') + { + // End of a enclosing scope + break; + } + else if (*p == '$') + { + // We have a prompt variable to print + ++p; + if (*p == '{') + { + ++p; + const char *var_name_begin = p; + const char *var_name_end = ::strchr (p, '}'); + + if (var_name_end && var_name_begin < var_name_end) + { + // if we have already failed to parse, skip this variable + if (success) + { + const char *cstr = NULL; + std::string token_format; + Address format_addr; + bool calculate_format_addr_function_offset = false; + // Set reg_kind and reg_num to invalid values + RegisterKind reg_kind = kNumRegisterKinds; + uint32_t reg_num = LLDB_INVALID_REGNUM; + FileSpec format_file_spec; + const RegisterInfo *reg_info = NULL; + RegisterContext *reg_ctx = NULL; + bool do_deref_pointer = false; + ValueObject::ExpressionPathScanEndReason reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + ValueObject::ExpressionPathEndResultType final_value_type = ValueObject::eExpressionPathEndResultTypePlain; + + // Each variable must set success to true below... + bool var_success = false; + switch (var_name_begin[0]) + { + case '*': + case 'v': + case 's': + { + if (!valobj) + break; + + if (log) + log->Printf("[Debugger::FormatPrompt] initial string: %s",var_name_begin); + + // check for *var and *svar + if (*var_name_begin == '*') + { + do_deref_pointer = true; + var_name_begin++; + if (log) + log->Printf("[Debugger::FormatPrompt] found a deref, new string is: %s",var_name_begin); + } + + if (*var_name_begin == 's') + { + if (!valobj->IsSynthetic()) + valobj = valobj->GetSyntheticValue().get(); + if (!valobj) + break; + var_name_begin++; + if (log) + log->Printf("[Debugger::FormatPrompt] found a synthetic, new string is: %s",var_name_begin); + } + + // should be a 'v' by now + if (*var_name_begin != 'v') + break; + + if (log) + log->Printf("[Debugger::FormatPrompt] string I am working with: %s",var_name_begin); + + ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? + ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); + ValueObject::GetValueForExpressionPathOptions options; + options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().DoAllowSyntheticChildren(); + ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eValueObjectRepresentationStyleSummary; + ValueObject* target = NULL; + Format custom_format = eFormatInvalid; + const char* var_name_final = NULL; + const char* var_name_final_if_array_range = NULL; + const char* close_bracket_position = NULL; + int64_t index_lower = -1; + int64_t index_higher = -1; + bool is_array_range = false; + const char* first_unparsed; + bool was_plain_var = false; + bool was_var_format = false; + bool was_var_indexed = false; + + if (!valobj) break; + // simplest case ${var}, just print valobj's value + if (IsToken (var_name_begin, "var}")) + { + was_plain_var = true; + target = valobj; + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + else if (IsToken (var_name_begin,"var%")) + { + was_var_format = true; + // this is a variable with some custom format applied to it + const char* percent_position; + target = valobj; + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + ScanFormatDescriptor (var_name_begin, + var_name_end, + &var_name_final, + &percent_position, + &custom_format, + &val_obj_display); + } + // this is ${var.something} or multiple .something nested + else if (IsToken (var_name_begin, "var")) + { + if (IsToken (var_name_begin, "var[")) + was_var_indexed = true; + const char* percent_position; + ScanFormatDescriptor (var_name_begin, + var_name_end, + &var_name_final, + &percent_position, + &custom_format, + &val_obj_display); + + const char* open_bracket_position; + const char* separator_position; + ScanBracketedRange (var_name_begin, + var_name_end, + var_name_final, + &open_bracket_position, + &separator_position, + &close_bracket_position, + &var_name_final_if_array_range, + &index_lower, + &index_higher); + + Error error; + + std::string expr_path(var_name_final-var_name_begin-1,0); + memcpy(&expr_path[0], var_name_begin+3,var_name_final-var_name_begin-3); + + if (log) + log->Printf("[Debugger::FormatPrompt] symbol to expand: %s",expr_path.c_str()); + + target = valobj->GetValueForExpressionPath(expr_path.c_str(), + &first_unparsed, + &reason_to_stop, + &final_value_type, + options, + &what_next).get(); + + if (!target) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + break; + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] ALL RIGHT: unparsed portion = %s, why stopping = %d," + " final_value_type %d", + first_unparsed, reason_to_stop, final_value_type); + } + } + else + break; + + 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 + Error error; + target = target->Dereference(error).get(); + if (error.Fail()) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR: %s\n", error.AsCString("unknown")); \ + break; + } + do_deref_pointer = 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()); + lldb::TypeNameSpecifierImplSP type_sp(new TypeNameSpecifierImpl(bitfield_name.GetData(),false)); + if (!DataVisualization::GetSummaryForType(type_sp)) + val_obj_display = ValueObject::eValueObjectRepresentationStyleValue; + } + + // TODO use flags for these + const uint32_t type_info_flags = target->GetClangType().GetTypeInfo(NULL); + bool is_array = (type_info_flags & ClangASTType::eTypeIsArray) != 0; + bool is_pointer = (type_info_flags & ClangASTType::eTypeIsPointer) != 0; + bool is_aggregate = target->GetClangType().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 + var_success = target->DumpPrintableRepresentation(str_temp, + val_obj_display, + custom_format); + if (log) + log->Printf("[Debugger::FormatPrompt] special cases did%s match", var_success ? "" : "n't"); + + // should not happen + if (var_success) + s << str_temp.GetData(); + var_success = true; + break; + } + 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::ePrintableRepresentationSpecialCasesDisable); + } + var_success = true; + break; + } + } + + // 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(); + var_success = true; + break; + } + + // 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 << ""; + var_success = true; + break; + } + + if (!is_array_range) + { + if (log) + log->Printf("[Debugger::FormatPrompt] dumping ordinary printable output"); + var_success = 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) + break; + if (log) + log->Printf("[Debugger::FormatPrompt] handle as array"); + const char* special_directions = NULL; + StreamString special_directions_writer; + if (close_bracket_position && (var_name_end-close_bracket_position > 1)) + { + ConstString additional_data; + additional_data.SetCStringWithLength(close_bracket_position+1, var_name_end-close_bracket_position-1); + special_directions_writer.Printf("${%svar%s}", + do_deref_pointer ? "*" : "", + additional_data.GetCString()); + special_directions = special_directions_writer.GetData(); + } + + // let us display items index_lower thru index_higher of this array + s.PutChar('['); + var_success = true; + + if (index_higher < 0) + index_higher = valobj->GetNumChildren() - 1; + + uint32_t max_num_children = target->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + for (;index_lower<=index_higher;index_lower++) + { + ValueObject* item = ExpandIndexedExpression (target, + index_lower, + exe_ctx->GetFramePtr(), + false).get(); + + if (!item) + { + if (log) + log->Printf("[Debugger::FormatPrompt] ERROR in getting child item at index %" PRId64, index_lower); + } + else + { + if (log) + log->Printf("[Debugger::FormatPrompt] special_directions for child item: %s",special_directions); + } + + if (!special_directions) + var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); + else + var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item); + + if (--max_num_children == 0) + { + s.PutCString(", ..."); + break; + } + + if (index_lower < index_higher) + s.PutChar(','); + } + s.PutChar(']'); + } + } + break; + case 'a': + if (IsToken (var_name_begin, "addr}")) + { + if (addr && addr->IsValid()) + { + var_success = true; + format_addr = *addr; + } + } + break; + + case 'p': + if (IsToken (var_name_begin, "process.")) + { + if (exe_ctx) + { + Process *process = exe_ctx->GetProcessPtr(); + if (process) + { + var_name_begin += ::strlen ("process."); + if (IsTokenWithFormat (var_name_begin, "id", token_format, "%" PRIu64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), process->GetID()); + var_success = true; + } + else if ((IsToken (var_name_begin, "name}")) || + (IsToken (var_name_begin, "file.basename}")) || + (IsToken (var_name_begin, "file.fullpath}"))) + { + Module *exe_module = process->GetTarget().GetExecutableModulePointer(); + if (exe_module) + { + if (var_name_begin[0] == 'n' || var_name_begin[5] == 'f') + { + format_file_spec.GetFilename() = exe_module->GetFileSpec().GetFilename(); + var_success = format_file_spec; + } + else + { + format_file_spec = exe_module->GetFileSpec(); + var_success = format_file_spec; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = process->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, process, script_name)) + var_success = true; + } + } + } + } + break; + + case 't': + if (IsToken (var_name_begin, "thread.")) + { + if (exe_ctx) + { + Thread *thread = exe_ctx->GetThreadPtr(); + if (thread) + { + var_name_begin += ::strlen ("thread."); + if (IsTokenWithFormat (var_name_begin, "id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), thread->GetID()); + var_success = true; + } + else if (IsTokenWithFormat (var_name_begin, "protocol_id", token_format, "0x%4.4" PRIx64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), thread->GetProtocolID()); + var_success = true; + } + else if (IsTokenWithFormat (var_name_begin, "index", token_format, "%" PRIu64, exe_ctx, sc)) + { + s.Printf(token_format.c_str(), (uint64_t)thread->GetIndexID()); + var_success = true; + } + else if (IsToken (var_name_begin, "name}")) + { + cstr = thread->GetName(); + var_success = cstr && cstr[0]; + if (var_success) + s.PutCString(cstr); + } + else if (IsToken (var_name_begin, "queue}")) + { + cstr = thread->GetQueueName(); + var_success = cstr && cstr[0]; + if (var_success) + s.PutCString(cstr); + } + else if (IsToken (var_name_begin, "stop-reason}")) + { + StopInfoSP stop_info_sp = thread->GetStopInfo (); + if (stop_info_sp && stop_info_sp->IsValid()) + { + cstr = stop_info_sp->GetDescription(); + if (cstr && cstr[0]) + { + s.PutCString(cstr); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "return-value}")) + { + 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) + { + ValueObject::DumpValueObject (s, return_valobj_sp.get()); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = thread->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, thread, script_name)) + var_success = true; + } + } + } + } + else if (IsToken (var_name_begin, "target.")) + { + // TODO: hookup properties +// if (!target_properties_sp) +// { +// Target *target = Target::GetTargetFromContexts (exe_ctx, sc); +// if (target) +// target_properties_sp = target->GetProperties(); +// } +// +// if (target_properties_sp) +// { +// var_name_begin += ::strlen ("target."); +// const char *end_property = strchr(var_name_begin, '}'); +// if (end_property) +// { +// ConstString property_name(var_name_begin, end_property - var_name_begin); +// std::string property_value (target_properties_sp->GetPropertyValue(property_name)); +// if (!property_value.empty()) +// { +// s.PutCString (property_value.c_str()); +// var_success = true; +// } +// } +// } + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + if (target) + { + var_name_begin += ::strlen ("target."); + if (IsToken (var_name_begin, "arch}")) + { + ArchSpec arch (target->GetArchitecture ()); + if (arch.IsValid()) + { + s.PutCString (arch.GetArchitectureName()); + var_success = true; + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = target->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, target, script_name)) + var_success = true; + } + } + } + break; + + + case 'm': + if (IsToken (var_name_begin, "module.")) + { + if (sc && sc->module_sp.get()) + { + Module *module = sc->module_sp.get(); + var_name_begin += ::strlen ("module."); + + if (IsToken (var_name_begin, "file.")) + { + if (module->GetFileSpec()) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = module->GetFileSpec().GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = module->GetFileSpec(); + var_success = format_file_spec; + } + } + } + } + } + break; + + + case 'f': + if (IsToken (var_name_begin, "file.")) + { + if (sc && sc->comp_unit != NULL) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = sc->comp_unit->GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = *sc->comp_unit; + var_success = format_file_spec; + } + } + } + else if (IsToken (var_name_begin, "frame.")) + { + if (exe_ctx) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) + { + var_name_begin += ::strlen ("frame."); + if (IsToken (var_name_begin, "index}")) + { + s.Printf("%u", frame->GetFrameIndex()); + var_success = true; + } + else if (IsToken (var_name_begin, "pc}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_PC; + var_success = true; + } + else if (IsToken (var_name_begin, "sp}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_SP; + var_success = true; + } + else if (IsToken (var_name_begin, "fp}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FP; + var_success = true; + } + else if (IsToken (var_name_begin, "flags}")) + { + reg_kind = eRegisterKindGeneric; + reg_num = LLDB_REGNUM_GENERIC_FLAGS; + var_success = true; + } + else if (IsToken (var_name_begin, "reg.")) + { + reg_ctx = frame->GetRegisterContext().get(); + if (reg_ctx) + { + var_name_begin += ::strlen ("reg."); + if (var_name_begin < var_name_end) + { + std::string reg_name (var_name_begin, var_name_end); + reg_info = reg_ctx->GetRegisterInfoByName (reg_name.c_str()); + if (reg_info) + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "script:")) + { + var_name_begin += ::strlen("script:"); + std::string script_name(var_name_begin,var_name_end); + ScriptInterpreter* script_interpreter = frame->GetThread()->GetProcess()->GetTarget().GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (RunScriptFormatKeyword (s, script_interpreter, frame, script_name)) + var_success = true; + } + } + } + } + else if (IsToken (var_name_begin, "function.")) + { + if (sc && (sc->function != NULL || sc->symbol != NULL)) + { + var_name_begin += ::strlen ("function."); + if (IsToken (var_name_begin, "id}")) + { + if (sc->function) + s.Printf("function{0x%8.8" PRIx64 "}", sc->function->GetID()); + else + s.Printf("symbol[%u]", sc->symbol->GetID()); + + var_success = true; + } + else if (IsToken (var_name_begin, "name}")) + { + if (sc->function) + cstr = sc->function->GetName().AsCString (NULL); + else if (sc->symbol) + cstr = sc->symbol->GetName().AsCString (NULL); + if (cstr) + { + s.PutCString(cstr); + + 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().Dump(&s); + } + } + } + var_success = true; + } + } + else if (IsToken (var_name_begin, "name-with-args}")) + { + // Print the function name with arguments in it + + if (sc->function) + { + var_success = true; + ExecutionContextScope *exe_scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL; + cstr = sc->function->GetName().AsCString (NULL); + if (cstr) + { + const InlineFunctionInfo *inline_info = NULL; + 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().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 = NULL; + 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) + { + VariableSP var_sp (args.GetVariableAtIndex (arg_idx)); + ValueObjectSP var_value_sp (ValueObjectVariable::Create (exe_scope, var_sp)); + const char *var_name = var_value_sp->GetName().GetCString(); + const char *var_value = var_value_sp->GetValueAsCString(); + if (arg_idx > 0) + s.PutCString (", "); + if (var_value_sp->GetError().Success()) + { + if (var_value) + s.Printf ("%s=%s", var_name, var_value); + else + s.Printf ("%s=%s at %s", var_name, var_value_sp->GetTypeName().GetCString(), var_value_sp->GetLocationAsCString()); + } + else + s.Printf ("%s=", var_name); + } + + if (close_paren) + s.PutCString (close_paren); + else + s.PutChar(')'); + + } + else + { + s.PutCString(cstr); + } + } + } + else if (sc->symbol) + { + cstr = sc->symbol->GetName().AsCString (NULL); + if (cstr) + { + s.PutCString(cstr); + var_success = true; + } + } + } + else if (IsToken (var_name_begin, "addr-offset}")) + { + var_success = addr != NULL; + if (var_success) + { + format_addr = *addr; + calculate_format_addr_function_offset = true; + } + } + else if (IsToken (var_name_begin, "line-offset}")) + { + var_success = sc->line_entry.range.GetBaseAddress().IsValid(); + if (var_success) + { + format_addr = sc->line_entry.range.GetBaseAddress(); + calculate_format_addr_function_offset = true; + } + } + else if (IsToken (var_name_begin, "pc-offset}")) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + var_success = frame != NULL; + if (var_success) + { + format_addr = frame->GetFrameCodeAddress(); + calculate_format_addr_function_offset = true; + } + } + } + } + break; + + case 'l': + if (IsToken (var_name_begin, "line.")) + { + if (sc && sc->line_entry.IsValid()) + { + var_name_begin += ::strlen ("line."); + if (IsToken (var_name_begin, "file.")) + { + var_name_begin += ::strlen ("file."); + + if (IsToken (var_name_begin, "basename}")) + { + format_file_spec.GetFilename() = sc->line_entry.file.GetFilename(); + var_success = format_file_spec; + } + else if (IsToken (var_name_begin, "fullpath}")) + { + format_file_spec = sc->line_entry.file; + var_success = format_file_spec; + } + } + else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64, exe_ctx, sc)) + { + var_success = true; + s.Printf(token_format.c_str(), (uint64_t)sc->line_entry.line); + } + else if ((IsToken (var_name_begin, "start-addr}")) || + (IsToken (var_name_begin, "end-addr}"))) + { + var_success = sc && sc->line_entry.range.GetBaseAddress().IsValid(); + if (var_success) + { + format_addr = sc->line_entry.range.GetBaseAddress(); + if (var_name_begin[0] == 'e') + format_addr.Slide (sc->line_entry.range.GetByteSize()); + } + } + } + } + break; + } + + if (var_success) + { + // If format addr is valid, then we need to print an address + if (reg_num != LLDB_INVALID_REGNUM) + { + StackFrame *frame = exe_ctx->GetFramePtr(); + // We have a register value to display... + if (reg_num == LLDB_REGNUM_GENERIC_PC && reg_kind == eRegisterKindGeneric) + { + format_addr = frame->GetFrameCodeAddress(); + } + else + { + if (reg_ctx == NULL) + reg_ctx = frame->GetRegisterContext().get(); + + if (reg_ctx) + { + if (reg_kind != kNumRegisterKinds) + reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber(reg_kind, reg_num); + reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_num); + var_success = reg_info != NULL; + } + } + } + + if (reg_info != NULL) + { + RegisterValue reg_value; + var_success = reg_ctx->ReadRegister (reg_info, reg_value); + if (var_success) + { + reg_value.Dump(&s, reg_info, false, false, eFormatDefault); + } + } + + if (format_file_spec) + { + s << format_file_spec; + } + + // If format addr is valid, then we need to print an address + if (format_addr.IsValid()) + { + var_success = false; + + if (calculate_format_addr_function_offset) + { + Address func_addr; + + if (sc) + { + if (sc->function) + { + func_addr = sc->function->GetAddressRange().GetBaseAddress(); + if (sc->block) + { + // 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->GetAddress(); + } + + if (func_addr.IsValid()) + { + 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) + s.Printf(" + %" PRIu64, addr_file_addr - func_file_addr); + else if (addr_file_addr < func_file_addr) + s.Printf(" - %" PRIu64, func_file_addr - addr_file_addr); + var_success = 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) + s.Printf(" + %" PRIu64, addr_load_addr - func_load_addr); + else if (addr_load_addr < func_load_addr) + s.Printf(" - %" PRIu64, func_load_addr - addr_load_addr); + var_success = true; + } + } + } + } + else + { + Target *target = Target::GetTargetFromContexts (exe_ctx, sc); + addr_t vaddr = LLDB_INVALID_ADDRESS; + if (exe_ctx && !target->GetSectionLoadList().IsEmpty()) + vaddr = format_addr.GetLoadAddress (target); + if (vaddr == LLDB_INVALID_ADDRESS) + vaddr = format_addr.GetFileAddress (); + + if (vaddr != LLDB_INVALID_ADDRESS) + { + int addr_width = target->GetArchitecture().GetAddressByteSize() * 2; + if (addr_width == 0) + addr_width = 16; + s.Printf("0x%*.*" PRIx64, addr_width, addr_width, vaddr); + var_success = true; + } + } + } + } + + if (var_success == false) + success = false; + } + p = var_name_end; + } + else + break; + } + else + { + // We got a dollar sign with no '{' after it, it must just be a dollar sign + s.PutChar(*p); + } + } + else if (*p == '\\') + { + ++p; // skip the slash + switch (*p) + { + case 'a': s.PutChar ('\a'); break; + case 'b': s.PutChar ('\b'); break; + case 'f': s.PutChar ('\f'); break; + case 'n': s.PutChar ('\n'); break; + case 'r': s.PutChar ('\r'); break; + case 't': s.PutChar ('\t'); break; + case 'v': s.PutChar ('\v'); break; + case '\'': s.PutChar ('\''); break; + case '\\': s.PutChar ('\\'); 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, NULL, 8); + if (octal_value <= UINT8_MAX) + { + s.PutChar((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] = { 0,0,0 }; + hex_str[0] = *p; + if (isxdigit(p[1])) + { + ++p; // Skip the first of the two hex chars + hex_str[1] = *p; + } + + unsigned long hex_value = strtoul (hex_str, NULL, 16); + if (hex_value <= UINT8_MAX) + s.PutChar ((char)hex_value); + } + else + { + s.PutChar('x'); + } + break; + + default: + // Just desensitize any other character by just printing what + // came after the '\' + s << *p; + break; + + } + + } + } + if (end) + *end = p; + return success; +} + +bool +Debugger::FormatPrompt +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + ValueObject* valobj +) +{ + bool use_color = exe_ctx ? exe_ctx->GetTargetRef().GetDebugger().GetUseColor() : true; + std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); + if (format_str.length()) + format = format_str.c_str(); + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj); +} + +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.reset (new StreamCallback (log_callback, baton)); +} + +bool +Debugger::EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream) +{ + Log::Callbacks log_callbacks; + + StreamSP 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 == NULL || *log_file == '\0') + { + log_stream_sp.reset(new StreamFile(GetOutputFile().GetDescriptor(), false)); + } + else + { + LogStreamMap::iterator pos = m_log_streams.find(log_file); + if (pos != m_log_streams.end()) + log_stream_sp = pos->second.lock(); + if (!log_stream_sp) + { + log_stream_sp.reset (new StreamFile (log_file)); + m_log_streams[log_file] = log_stream_sp; + } + } + assert (log_stream_sp.get()); + + if (log_options == 0) + log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; + + if (Log::GetLogChannelCallbacks (ConstString(channel), log_callbacks)) + { + log_callbacks.enable (log_stream_sp, log_options, categories, &error_stream); + return true; + } + else + { + LogChannelSP log_channel_sp (LogChannel::FindPlugin (channel)); + if (log_channel_sp) + { + if (log_channel_sp->Enable (log_stream_sp, log_options, &error_stream, categories)) + { + return true; + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + return false; +} + +SourceManager & +Debugger::GetSourceManager () +{ + if (m_source_manager_ap.get() == NULL) + m_source_manager_ap.reset (new SourceManager (shared_from_this())); + return *m_source_manager_ap; +} + + diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp new file mode 100644 index 00000000000..e80e92c91b5 --- /dev/null +++ b/source/Core/Disassembler.cpp @@ -0,0 +1,1269 @@ +//===-- Disassembler.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/Disassembler.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/EmulateInstruction.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Timer.h" +#include "lldb/Interpreter/OptionValue.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Interpreter/OptionValueString.h" +#include "lldb/Interpreter/OptionValueUInt64.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.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) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, + "Disassembler::FindPlugin (arch = %s, plugin_name = %s)", + arch.GetArchitectureName(), + plugin_name); + + DisassemblerCreateInstance create_callback = NULL; + + 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.get()) + return disassembler_sp; + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDisassemblerCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + DisassemblerSP disassembler_sp(create_callback(arch, flavor)); + + if (disassembler_sp.get()) + 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 == NULL) + { + // 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) + { + if (target->GetSectionLoadList().IsEmpty()) + { + target->GetImages().ResolveFileAddress (addr.GetOffset(), resolved_addr); + } + else + { + target->GetSectionLoadList().ResolveLoadAddress (addr.GetOffset(), resolved_addr); + } + // We weren't able to resolve the address, just treat it as a + // raw address + if (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, + 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; iFindFunctions (name, + NULL, + 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, + 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 +) +{ + 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) + { + const bool prefer_file_cache = false; + size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, 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, + 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.get()) + { + 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; + + bool result = PrintInstructions (disasm_sp.get(), + debugger, + arch, + exe_ctx, + num_instructions, + num_mixed_context_lines, + options, + strm); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + return result; + } + } + 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, + 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.get()) + { + 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; + bool result = PrintInstructions (disasm_sp.get(), + debugger, + arch, + exe_ctx, + num_instructions, + num_mixed_context_lines, + options, + strm); + + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disasm_sp->GetInstructionList().Clear(); + return result; + } + } + return false; +} + +bool +Disassembler::PrintInstructions +( + Disassembler *disasm_ptr, + Debugger &debugger, + const ArchSpec &arch, + const ExecutionContext &exe_ctx, + uint32_t num_instructions, + 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 (); + uint32_t offset = 0; + SymbolContext sc; + SymbolContext prev_sc; + AddressRange sc_range; + const Address *pc_addr_ptr = NULL; + ExecutionContextScope *exe_scope = exe_ctx.GetBestExecutionContextScope(); + 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; + for (size_t i=0; iGetInstructionList().GetInstructionAtIndex (i).get(); + if (inst) + { + const Address &addr = inst->GetAddress(); + const bool inst_is_at_pc = pc_addr_ptr && addr == *pc_addr_ptr; + + 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 (num_mixed_context_lines) + { + if (!sc_range.ContainsFileAddress (addr)) + { + sc.GetAddressRange (scope, 0, use_inline_block_range, sc_range); + + if (sc != prev_sc) + { + if (offset != 0) + strm.EOL(); + + sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false); + strm.EOL(); + + if (sc.comp_unit && sc.line_entry.IsValid()) + { + source_manager.DisplaySourceLinesWithLineNumbers (sc.line_entry.file, + sc.line_entry.line, + num_mixed_context_lines, + num_mixed_context_lines, + ((inst_is_at_pc && (options & eOptionMarkPCSourceLine)) ? "->" : ""), + &strm); + } + } + } + } + else if ((sc.function || sc.symbol) && (sc.function != prev_sc.function || sc.symbol != prev_sc.symbol)) + { + if (prev_sc.function || prev_sc.symbol) + strm.EOL(); + + bool show_fullpaths = false; + bool show_module = true; + bool show_inlined_frames = true; + sc.DumpStopContext (&strm, + exe_scope, + addr, + show_fullpaths, + show_module, + show_inlined_frames); + + strm << ":\n"; + } + } + else + { + sc.Clear(true); + } + } + + if ((options & eOptionMarkPCAddress) && pc_addr_ptr) + { + strm.PutCString(inst_is_at_pc ? "-> " : " "); + } + const bool show_bytes = (options & eOptionShowBytes) != 0; + inst->Dump(&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx); + 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, + 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->GetAddress(); + 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, + 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() +{ +} + +AddressClass +Instruction::GetAddressClass () +{ + if (m_address_class == eAddressClassInvalid) + 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) +{ + size_t opcode_column_width = 7; + const size_t operand_column_width = 25; + + CalculateMnemonicOperandsAndCommentIfNeeded (exe_ctx); + + StreamString ss; + + if (show_address) + { + m_address.Dump(&ss, + exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL, + Address::DumpStyleLoadAddress, + Address::DumpStyleModuleWithFileAddress, + 0); + + ss.PutCString(": "); + } + + 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 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.GetSize(); + + // 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.c_str()); + ss.FillLastLineToColumn (opcode_pos + opcode_column_width, ' '); + ss.PutCString (m_mnemonics.c_str()); + + if (!m_comment.empty()) + { + ss.FillLastLineToColumn (opcode_pos + opcode_column_width + operand_column_width, ' '); + ss.PutCString (" ; "); + ss.PutCString (m_comment.c_str()); + } + s->Write (ss.GetData(), ss.GetSize()); +} + +bool +Instruction::DumpEmulation (const ArchSpec &arch) +{ + std::unique_ptr insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + { + insn_emulator_ap->SetInstruction (GetOpcode(), GetAddress(), NULL); + return insn_emulator_ap->EvaluateInstruction (0); + } + + return false; +} + +OptionValueSP +Instruction::ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type data_type) +{ + bool done = false; + char buffer[1024]; + + OptionValueSP option_value_sp (new 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.size() > 0) + { + std::string value; + static RegularExpression g_reg_exp ("^[ \t]*([^ \t]+)[ \t]*$"); + RegularExpression::Match regex_match(1); + bool reg_exp_success = g_reg_exp.Execute (line.c_str(), ®ex_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.reset (new OptionValueUInt64 (0, 0)); + data_value_sp->SetValueFromCString (value.c_str()); + break; + // Other types can be added later as needed. + default: + data_value_sp.reset (new 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]; + + OptionValueSP option_value_sp (new 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.size() > 0) + { + static RegularExpression g_reg_exp ("^[ \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.c_str(), ®ex_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.get() == NULL) + { + 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.get() == NULL) + { + 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.reset (new OptionValueUInt64 (0, 0)); + value_sp->SetValueFromCString (value.c_str()); + } + else + { + size_t len = value.size(); + if ((value[0] == '"') && (value[len-1] == '"')) + value = value.substr (1, len-2); + value_sp.reset (new 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 = 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.get() == NULL) + { + 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.get() == NULL) + { + 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.get() == NULL) + { + 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 insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + success = insn_emulator_ap->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 insn_emulator_ap (EmulateInstruction::FindPlugin (arch, eInstructionTypeAny, NULL)); + if (insn_emulator_ap.get()) + { + insn_emulator_ap->SetBaton (baton); + insn_emulator_ap->SetCallbacks (read_mem_callback, write_mem_callback, read_reg_callback, write_reg_callback); + insn_emulator_ap->SetInstruction (GetOpcode(), GetAddress(), NULL); + return insn_emulator_ap->EvaluateInstruction (evaluate_options); + } + + return false; +} + + +uint32_t +Instruction::GetData (DataExtractor &data) +{ + return m_opcode.GetData(data); +} + +InstructionList::InstructionList() : + m_instructions() +{ +} + +InstructionList::~InstructionList() +{ +} + +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; + 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); + } +} + + +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) const +{ + size_t num_instructions = m_instructions.size(); + + uint32_t next_branch = UINT32_MAX; + for (size_t i = start; i < num_instructions; i++) + { + if (m_instructions[i]->DoesBranch()) + { + next_branch = i; + break; + } + } + return next_branch; +} + +uint32_t +InstructionList::GetIndexOfInstructionAtLoadAddress (lldb::addr_t load_addr, Target &target) +{ + Address address; + address.SetLoadAddress(load_addr, &target); + 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; +} + +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 == NULL || byte_size == 0 || !range.GetBaseAddress().IsValid()) + return 0; + + DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); + DataBufferSP data_sp(heap_buffer); + + Error error; + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + const size_t bytes_read = target->ReadMemory (range.GetBaseAddress(), + prefer_file_cache, + heap_buffer->GetBytes(), + heap_buffer->GetByteSize(), + error, + &load_addr); + + if (bytes_read > 0) + { + if (bytes_read != heap_buffer->GetByteSize()) + heap_buffer->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 == NULL || 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 == NULL || byte_size == 0) + return 0; + + DataBufferHeap *heap_buffer = new DataBufferHeap (byte_size, '\0'); + DataBufferSP data_sp (heap_buffer); + + Error 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 == NULL) + m_flavor.assign("default"); + else + m_flavor.assign(flavor); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Disassembler::~Disassembler() +{ +} + +InstructionList & +Disassembler::GetInstructionList () +{ + return m_instruction_list; +} + +const InstructionList & +Disassembler::GetInstructionList () const +{ + return m_instruction_list; +} + +//---------------------------------------------------------------------- +// Class PseudoInstruction +//---------------------------------------------------------------------- +PseudoInstruction::PseudoInstruction () : + Instruction (Address(), eAddressClassUnknown), + m_description () +{ +} + +PseudoInstruction::~PseudoInstruction () +{ +} + +bool +PseudoInstruction::DoesBranch () +{ + // 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); + break; + } + case 16: + { + uint16_t value16 = *((uint16_t *) opcode_data); + m_opcode.SetOpcode16 (value16); + break; + } + case 32: + { + uint32_t value32 = *((uint32_t *) opcode_data); + m_opcode.SetOpcode32 (value32); + break; + } + case 64: + { + uint64_t value64 = *((uint64_t *) opcode_data); + m_opcode.SetOpcode64 (value64); + break; + } + default: + break; + } +} + +void +PseudoInstruction::SetDescription (const char *description) +{ + if (description && strlen (description) > 0) + m_description = description; +} diff --git a/source/Core/DynamicLoader.cpp b/source/Core/DynamicLoader.cpp new file mode 100644 index 00000000000..82f84048b32 --- /dev/null +++ b/source/Core/DynamicLoader.cpp @@ -0,0 +1,76 @@ +//===-- DynamicLoader.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Target/Process.h" +#include "lldb/Core/PluginManager.h" + +using namespace lldb; +using namespace lldb_private; + +DynamicLoader* +DynamicLoader::FindPlugin (Process *process, const char *plugin_name) +{ + DynamicLoaderCreateInstance create_callback = NULL; + if (plugin_name) + { + ConstString const_plugin_name(plugin_name); + create_callback = PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + std::unique_ptr instance_ap(create_callback(process, true)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + else + { + for (uint32_t idx = 0; (create_callback = PluginManager::GetDynamicLoaderCreateCallbackAtIndex(idx)) != NULL; ++idx) + { + std::unique_ptr instance_ap(create_callback(process, false)); + if (instance_ap.get()) + return instance_ap.release(); + } + } + return NULL; +} + + +//---------------------------------------------------------------------- +// DynamicLoader constructor +//---------------------------------------------------------------------- +DynamicLoader::DynamicLoader(Process *process) : + m_process (process) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DynamicLoader::~DynamicLoader() +{ +} + +//---------------------------------------------------------------------- +// 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); +} + diff --git a/source/Core/EmulateInstruction.cpp b/source/Core/EmulateInstruction.cpp new file mode 100644 index 00000000000..bf6c6d88b56 --- /dev/null +++ b/source/Core/EmulateInstruction.cpp @@ -0,0 +1,670 @@ +//===-- EmulateInstruction.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/EmulateInstruction.h" + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Endian.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" + +using namespace lldb; +using namespace lldb_private; + +EmulateInstruction* +EmulateInstruction::FindPlugin (const ArchSpec &arch, InstructionType supported_inst_type, const char *plugin_name) +{ + EmulateInstructionCreateInstance create_callback = NULL; + 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)) != NULL; ++idx) + { + EmulateInstruction *emulate_insn_ptr = create_callback(arch, supported_inst_type); + if (emulate_insn_ptr) + return emulate_insn_ptr; + } + } + return NULL; +} + +EmulateInstruction::EmulateInstruction (const ArchSpec &arch) : + m_arch (arch), + m_baton (NULL), + m_read_mem_callback (&ReadMemoryDefault), + m_write_mem_callback (&WriteMemoryDefault), + m_read_reg_callback (&ReadRegisterDefault), + m_write_reg_callback (&WriteRegisterDefault), + m_addr (LLDB_INVALID_ADDRESS) +{ + ::memset (&m_opcode, 0, sizeof (m_opcode)); +} + + +bool +EmulateInstruction::ReadRegister (const RegisterInfo *reg_info, RegisterValue& reg_value) +{ + if (m_read_reg_callback) + return m_read_reg_callback (this, m_baton, reg_info, reg_value); + return false; +} + +bool +EmulateInstruction::ReadRegister (uint32_t reg_kind, uint32_t reg_num, RegisterValue& reg_value) +{ + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return ReadRegister (®_info, reg_value); + return false; +} + +uint64_t +EmulateInstruction::ReadRegisterUnsigned (uint32_t 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) + return m_write_reg_callback (this, m_baton, context, reg_info, reg_value); + return false; +} + +bool +EmulateInstruction::WriteRegister (const Context &context, + uint32_t reg_kind, + uint32_t reg_num, + const RegisterValue& reg_value) +{ + RegisterInfo reg_info; + if (GetRegisterInfo(reg_kind, reg_num, reg_info)) + return WriteRegister (context, ®_info, reg_value); + return false; +} + + +bool +EmulateInstruction::WriteRegisterUnsigned (const Context &context, + uint32_t 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, ®_info, reg_value); + } + return false; +} + +bool +EmulateInstruction::WriteRegisterUnsigned (const Context &context, + const RegisterInfo *reg_info, + uint64_t uint_value) +{ + + if (reg_info) + { + 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) + 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.GetData(), uval_byte_size); + if (bytes_written == uval_byte_size) + return true; + return false; +} + +bool +EmulateInstruction::WriteMemory (const Context &context, + lldb::addr_t addr, + const void *src, + size_t src_len) +{ + if (m_write_mem_callback) + 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 || dst == NULL || dst_len == 0) + return 0; + + StackFrame *frame = (StackFrame *) baton; + + ProcessSP process_sp (frame->CalculateProcess()); + if (process_sp) + { + Error 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 || src == NULL || src_len == 0) + return 0; + + StackFrame *frame = (StackFrame *) baton; + + ProcessSP process_sp (frame->CalculateProcess()); + if (process_sp) + { + Error error; + return process_sp->WriteMemory (addr, src, src_len, error); + } + + return 0; +} + +bool +EmulateInstruction::ReadRegisterFrame (EmulateInstruction *instruction, + void *baton, + const RegisterInfo *reg_info, + RegisterValue ®_value) +{ + if (!baton) + 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 ®_value) +{ + if (!baton) + 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 ®_value) +{ + StreamFile strm (stdout, false); + strm.Printf (" Read Register (%s)\n", reg_info->name); + uint32_t reg_kind, 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 ®_value) +{ + StreamFile strm (stdout, false); + strm.Printf (" Write to Register (name = %s, value = " , reg_info->name); + reg_value.Dump(&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) + 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, + uint32_t ®_kind, + uint32_t ®_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[eRegisterKindGCC]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGCC; + return true; + } + + reg_num = reg_info->kinds[eRegisterKindGDB]; + if (reg_num != LLDB_INVALID_REGNUM) + { + reg_kind = eRegisterKindGDB; + return true; + } + return false; +} + +uint32_t +EmulateInstruction::GetInternalRegisterNumber (RegisterContext *reg_ctx, const RegisterInfo ®_info) +{ + uint32_t reg_kind, reg_num; + if (reg_ctx && GetBestRegisterKindAndNumber (®_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/source/Core/Error.cpp b/source/Core/Error.cpp new file mode 100644 index 00000000000..e29f12f0b2c --- /dev/null +++ b/source/Core/Error.cpp @@ -0,0 +1,399 @@ +//===-- Error.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "llvm/ADT/SmallVector.h" +#include +#include +#include + +#if defined (__arm__) && defined (__APPLE__) +#include +#endif + +using namespace lldb; +using namespace lldb_private; + +Error::Error (): + m_code (0), + m_type (eErrorTypeInvalid), + m_string () +{ +} + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +Error::Error(ValueType err, ErrorType type) : + m_code (err), + m_type (type), + m_string () +{ +} + +Error::Error (const Error &rhs) : + m_code (rhs.m_code), + m_type (rhs.m_type), + m_string (rhs.m_string) +{ +} + +Error::Error (const char* err_str): + m_code (0), + m_type (eErrorTypeInvalid), + m_string () +{ + SetErrorString(err_str); +} + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Error& +Error::operator = (const Error& rhs) +{ + if (this != &rhs) + { + m_code = rhs.m_code; + m_type = rhs.m_type; + m_string = rhs.m_string; + } + return *this; +} + + +//---------------------------------------------------------------------- +// Assignment operator +//---------------------------------------------------------------------- +const Error& +Error::operator = (uint32_t err) +{ + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); + return *this; +} + +Error::~Error() +{ +} + +//---------------------------------------------------------------------- +// 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 * +Error::AsCString(const char *default_error_str) const +{ + if (Success()) + return NULL; + + if (m_string.empty()) + { + const char *s = NULL; + switch (m_type) + { + case eErrorTypeMachKernel: +#if defined (__APPLE__) + s = ::mach_error_string (m_code); +#endif + break; + + case eErrorTypePOSIX: + s = ::strerror (m_code); + break; + + default: + break; + } + if (s) + m_string.assign(s); + } + if (m_string.empty()) + { + if (default_error_str) + m_string.assign(default_error_str); + else + return NULL; // User wanted a NULL string back... + } + return m_string.c_str(); +} + + +//---------------------------------------------------------------------- +// Clear the error and any cached error string that it might contain. +//---------------------------------------------------------------------- +void +Error::Clear () +{ + m_code = 0; + m_type = eErrorTypeGeneric; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Access the error value. +//---------------------------------------------------------------------- +Error::ValueType +Error::GetError () const +{ + return m_code; +} + +//---------------------------------------------------------------------- +// Access the error type. +//---------------------------------------------------------------------- +ErrorType +Error::GetType () const +{ + return m_type; +} + +//---------------------------------------------------------------------- +// Retuns true if this object contains an value that describes an +// error or otherwise non-success result. +//---------------------------------------------------------------------- +bool +Error::Fail () const +{ + return m_code != 0; +} + +//---------------------------------------------------------------------- +// Log the error given a string with format. If the this object +// contains an error code, update the error string to contain the +// "error: " followed by the formatted string, followed by the error +// value and any string that describes the current error. This +// allows more context to be given to an error string that remains +// cached in this object. Logging always occurs even when the error +// code contains a non-error value. +//---------------------------------------------------------------------- +void +Error::PutToLog (Log *log, const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + if (Fail()) + { + const char *err_str = AsCString(); + if (err_str == NULL) + err_str = "???"; + + SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + if (log) + log->Error("%s", m_string.c_str()); + } + else + { + if (log) + log->Printf("%s err = 0x%8.8x", arg_msg, m_code); + } + ::free (arg_msg); + } +} + +//---------------------------------------------------------------------- +// Log the error given a string with format. If the this object +// contains an error code, update the error string to contain the +// "error: " followed by the formatted string, followed by the error +// value and any string that describes the current error. This +// allows more context to be given to an error string that remains +// cached in this object. Logging only occurs even when the error +// code contains a error value. +//---------------------------------------------------------------------- +void +Error::LogIfError (Log *log, const char *format, ...) +{ + if (Fail()) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + const char *err_str = AsCString(); + if (err_str == NULL) + err_str = "???"; + + SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + if (log) + log->Error("%s", m_string.c_str()); + + ::free (arg_msg); + } + } +} + +//---------------------------------------------------------------------- +// Set accesssor for the error value to "err" and the type to +// "eErrorTypeMachKernel" +//---------------------------------------------------------------------- +void +Error::SetMachError (uint32_t err) +{ + m_code = err; + m_type = eErrorTypeMachKernel; + m_string.clear(); +} + +//---------------------------------------------------------------------- +// Set accesssor for the error value and type. +//---------------------------------------------------------------------- +void +Error::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 +Error::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 +Error::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 +Error::SetErrorString (const char *err_str) +{ + if (err_str && err_str[0]) + { + // 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; + } + else + m_string.clear(); +} + +//------------------------------------------------------------------ +/// Set the current error string to a formatted error string. +/// +/// @param format +/// A printf style format string +//------------------------------------------------------------------ +int +Error::SetErrorStringWithFormat (const char *format, ...) +{ + if (format && 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 +Error::SetErrorStringWithVarArg (const char *format, va_list args) +{ + if (format && format[0]) + { + // If we have an error string, we should always at least have + // an error set to a generic value. + if (Success()) + SetErrorToGenericError(); + + // Try and fit our error into a 1024 byte buffer first... + llvm::SmallVector buf; + buf.resize(1024); + // Copy in case our first call to vsnprintf doesn't fit into our + // allocated buffer above + va_list copy_args; + va_copy (copy_args, args); + unsigned length = ::vsnprintf (buf.data(), buf.size(), format, args); + if (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(), format, copy_args); + va_end (copy_args); + assert (length < buf.size()); + } + m_string.assign(buf.data(), length); + va_end (args); + return length; + } + else + { + m_string.clear(); + } + return 0; +} + + +//---------------------------------------------------------------------- +// Returns true if the error code in this object is considered a +// successful return value. +//---------------------------------------------------------------------- +bool +Error::Success() const +{ + return m_code == 0; +} + +bool +Error::WasInterrupted() const +{ + if (m_type == eErrorTypePOSIX && m_code == EINTR) + return true; + else + return false; +} + diff --git a/source/Core/Event.cpp b/source/Core/Event.cpp new file mode 100644 index 00000000000..2d4899dd6dc --- /dev/null +++ b/source/Core/Event.cpp @@ -0,0 +1,225 @@ +//===-- Event.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Event.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" +#include "lldb/Target/Process.h" +#include + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Event constructor +//---------------------------------------------------------------------- +Event::Event (Broadcaster *broadcaster, uint32_t event_type, EventData *data) : + m_broadcaster (broadcaster), + m_type (event_type), + m_data_ap (data) +{ +} + +Event::Event(uint32_t event_type, EventData *data) : + m_broadcaster (NULL), // Set by the broadcaster when this event gets broadcast + m_type (event_type), + m_data_ap (data) +{ +} + + +//---------------------------------------------------------------------- +// Event destructor +//---------------------------------------------------------------------- +Event::~Event () +{ +} + +void +Event::Dump (Stream *s) const +{ + if (m_broadcaster) + { + StreamString event_name; + if (m_broadcaster->GetEventNames (event_name, m_type, false)) + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x (%s), data = ", + this, + m_broadcaster, + m_broadcaster->GetBroadcasterName().GetCString(), + m_type, + event_name.GetString().c_str()); + else + s->Printf("%p Event: broadcaster = %p (%s), type = 0x%8.8x, data = ", + this, + m_broadcaster, + m_broadcaster->GetBroadcasterName().GetCString(), + m_type); + } + else + s->Printf("%p Event: broadcaster = NULL, type = 0x%8.8x, data = ", this, m_type); + + if (m_data_ap.get() == NULL) + s->Printf (""); + else + { + s->PutChar('{'); + m_data_ap->Dump (s); + s->PutChar('}'); + } +} + +void +Event::DoOnRemoval () +{ + if (m_data_ap.get()) + m_data_ap->DoOnRemoval (this); +} + +EventData::EventData() +{ +} + +EventData::~EventData() +{ +} + +void +EventData::Dump (Stream *s) const +{ + s->PutCString ("Generic Event Data"); +} + +EventDataBytes::EventDataBytes () : + m_bytes() +{ +} + +EventDataBytes::EventDataBytes (const char *cstr) : + m_bytes() +{ + SetBytesFromCString (cstr); +} + +EventDataBytes::EventDataBytes (const void *src, size_t src_len) : + m_bytes() +{ + SetBytes (src, src_len); +} + +EventDataBytes::~EventDataBytes() +{ +} + +const ConstString & +EventDataBytes::GetFlavorString () +{ + static ConstString g_flavor ("EventDataBytes"); + return g_flavor; +} + +const 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->Printf("\"%s\"", m_bytes.c_str()); + } + else if (m_bytes.size() > 0) + { + DataExtractor data; + data.SetData(&m_bytes[0], m_bytes.size(), lldb::endian::InlHostByteOrder()); + data.Dump(s, 0, eFormatBytes, 1, m_bytes.size(), 32, LLDB_INVALID_ADDRESS, 0, 0); + } +} + +const void * +EventDataBytes::GetBytes() const +{ + if (m_bytes.empty()) + return NULL; + return &m_bytes[0]; +} + +size_t +EventDataBytes::GetByteSize() const +{ + return m_bytes.size (); +} + +void +EventDataBytes::SetBytes (const void *src, size_t src_len) +{ + if (src && src_len > 0) + m_bytes.assign ((const char *)src, src_len); + else + m_bytes.clear(); +} + +void +EventDataBytes::SetBytesFromCString (const char *cstr) +{ + if (cstr && 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) + return e->GetBytes(); + return NULL; +} + +size_t +EventDataBytes::GetByteSizeFromEvent (const Event *event_ptr) +{ + const EventDataBytes *e = GetEventDataFromEvent (event_ptr); + if (e) + return e->GetByteSize(); + return 0; +} + +const EventDataBytes * +EventDataBytes::GetEventDataFromEvent (const Event *event_ptr) +{ + if (event_ptr) + { + const EventData *event_data = event_ptr->GetData(); + if (event_data && event_data->GetFlavor() == EventDataBytes::GetFlavorString()) + return static_cast (event_data); + } + return NULL; +} + +void +EventDataBytes::SwapBytes (std::string &new_bytes) +{ + m_bytes.swap (new_bytes); +} + + diff --git a/source/Core/FileLineResolver.cpp b/source/Core/FileLineResolver.cpp new file mode 100644 index 00000000000..15cbbe6ff9e --- /dev/null +++ b/source/Core/FileLineResolver.cpp @@ -0,0 +1,117 @@ +//===-- FileLineResolver.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/FileLineResolver.h" + +// Project includes +#include "lldb/lldb-private-log.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/LineTable.h" + +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, 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; +} + +Searcher::Depth +FileLineResolver::GetDepth() +{ + return Searcher::eDepthCompUnit; +} + +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/source/Core/FileSpecList.cpp b/source/Core/FileSpecList.cpp new file mode 100644 index 00000000000..0cec8faa6bc --- /dev/null +++ b/source/Core/FileSpecList.cpp @@ -0,0 +1,234 @@ +//===-- FileSpecList.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Stream.h" +#include + +using namespace lldb_private; +using namespace std; + +//------------------------------------------------------------------ +// Default constructor +//------------------------------------------------------------------ +FileSpecList::FileSpecList() : + m_files() +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpecList::FileSpecList(const FileSpecList& rhs) : + m_files(rhs.m_files) +{ +} + +//------------------------------------------------------------------ +// Destructor +//------------------------------------------------------------------ +FileSpecList::~FileSpecList() +{ +} + +//------------------------------------------------------------------ +// Assignment operator +//------------------------------------------------------------------ +const FileSpecList& +FileSpecList::operator= (const FileSpecList& rhs) +{ + if (this != &rhs) + m_files = rhs.m_files; + return *this; +} + +//------------------------------------------------------------------ +// 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 pos, 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 UINT32_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 (m_files[idx].GetFilename() == file_spec.GetFilename()) + 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 NULL; +} + +//------------------------------------------------------------------ +// 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) +{ +#if 0 // FIXME: Just sketching... + matches.Clear(); + FileSpec path_spec = FileSpec (path); + if (path_spec.Exists ()) + { + FileSpec::FileType type = path_spec.GetFileType(); + if (type == FileSpec::eFileTypeSymbolicLink) + // Shouldn't there be a Resolve on a file spec that real-path's it? + { + } + + if (type == FileSpec::eFileTypeRegular + || (type == FileSpec::eFileTypeDirectory && dir_okay)) + { + matches.Append (path_spec); + return 1; + } + else if (type == FileSpec::eFileTypeDirectory) + { + // Fill the match list with all the files in the directory: + + } + else + { + return 0; + } + + } + else + { + ConstString dir_name = path_spec.GetDirectory(); + Constring file_name = GetFilename(); + if (dir_name == NULL) + { + // Match files in the CWD. + } + else + { + // Match files in the given directory: + + } + } +#endif + return 0; +} diff --git a/source/Core/History.cpp b/source/Core/History.cpp new file mode 100644 index 00000000000..0105dce730d --- /dev/null +++ b/source/Core/History.cpp @@ -0,0 +1,26 @@ +//===-- History.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/History.h" + +// C Includes +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +using namespace lldb; +using namespace lldb_private; + +void +HistorySourceUInt::DumpHistoryEvent (Stream &strm, HistoryEvent event) +{ + strm.Printf ("%s %" PRIu64, m_name.c_str(), (uint64_t)((uintptr_t)event)); +} diff --git a/source/Core/InputReader.cpp b/source/Core/InputReader.cpp new file mode 100644 index 00000000000..cbaa671bcba --- /dev/null +++ b/source/Core/InputReader.cpp @@ -0,0 +1,387 @@ +//===-- InputReader.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include + +#include "lldb/Core/InputReader.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +InputReader::InputReader (Debugger &debugger) : + m_debugger (debugger), + m_callback (NULL), + m_callback_baton (NULL), + m_end_token (), + m_granularity (eInputReaderGranularityInvalid), + m_done (true), + m_echo (true), + m_active (false), + m_reader_done (false), + m_user_input(), + m_save_user_input(false) +{ +} + +InputReader::~InputReader () +{ +} + +Error +InputReader::Initialize +( + Callback callback, + void *baton, + lldb::InputReaderGranularity granularity, + const char *end_token, + const char *prompt, + bool echo +) +{ + Error err; + m_callback = callback; + m_callback_baton = baton, + m_granularity = granularity; + if (end_token != NULL) + m_end_token = end_token; + if (prompt != NULL) + m_prompt = prompt; + m_done = true; + m_echo = echo; + + if (m_granularity == eInputReaderGranularityInvalid) + { + err.SetErrorString ("Invalid read token size: Reader must be initialized with a token size other than 'eInputReaderGranularityInvalid'."); + } + else + if (end_token != NULL && granularity != eInputReaderGranularityInvalid) + { + if (granularity == eInputReaderGranularityByte) + { + // Check to see if end_token is longer than one byte. + + if (strlen (end_token) > 1) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (byte)."); + } + } + else if (granularity == eInputReaderGranularityWord) + { + // Check to see if m_end_token contains any white space (i.e. is multiple words). + + const char *white_space = " \t\n"; + size_t pos = m_end_token.find_first_of (white_space); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot be larger than specified token size (word)."); + } + } + else + { + // Check to see if m_end_token contains any newlines; cannot handle multi-line end tokens. + + size_t pos = m_end_token.find_first_of ('\n'); + if (pos != std::string::npos) + { + err.SetErrorString ("Invalid end token: End token cannot contain a newline."); + } + } + } + + m_done = err.Fail(); + + return err; +} + +size_t +InputReader::HandleRawBytes (const char *bytes, size_t bytes_len) +{ + const char *end_token = NULL; + + if (m_end_token.empty() == false) + { + end_token = ::strstr (bytes, m_end_token.c_str()); + if (end_token >= bytes + bytes_len) + end_token = NULL; + } + + const char *p = bytes; + const char *end = bytes + bytes_len; + + switch (m_granularity) + { + case eInputReaderGranularityInvalid: + break; + + case eInputReaderGranularityByte: + while (p < end) + { + if (end_token == p) + { + p += m_end_token.size(); + SetIsDone(true); + break; + } + + if (m_callback (m_callback_baton, *this, eInputReaderGotToken, p, 1) == 0) + break; + ++p; + if (IsDone()) + break; + } + // Return how many bytes were handled. + return p - bytes; + break; + + + case eInputReaderGranularityWord: + { + char quote = '\0'; + const char *word_start = NULL; + bool send_word = false; + for (; p < end; ++p, send_word = false) + { + if (end_token && end_token == p) + { + m_end_token.size(); + SetIsDone(true); + break; + } + + const char ch = *p; + if (isspace(ch) && (!quote || (quote == ch && p[-1] != '\\'))) + { + // We have a space character or the terminating quote + send_word = word_start != NULL; + quote = '\0'; + } + else if (quote) + { + // We are in the middle of a quoted character + continue; + } + else if (ch == '"' || ch == '\'' || ch == '`') + quote = ch; + else if (word_start == NULL) + { + // We have the first character in a word + word_start = p; + } + + if (send_word) + { + const size_t word_len = p - word_start; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + word_start, + word_len); + + if (bytes_handled != word_len) + return word_start - bytes + bytes_handled; + + if (IsDone()) + return p - bytes; + } + } + } + break; + + + case eInputReaderGranularityLine: + { + const char *line_start = bytes; + const char *end_line = NULL; + while (p < end) + { + const char ch = *p; + if (ch == '\n' || ch == '\r') + { + size_t line_length = p - line_start; + // Now skip the newline character + ++p; + // Skip a complete DOS newline if we run into one + if (ch == 0xd && p < end && *p == 0xa) + ++p; + + if (line_start <= end_token && end_token < line_start + line_length) + { + SetIsDone(true); + m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + end_token - line_start); + + return p - bytes; + } + + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + line_start, + line_length); + + end_line = p; + + if (bytes_handled != line_length) + { + // The input reader wasn't able to handle all the data + return line_start - bytes + bytes_handled; + } + + + if (IsDone()) + return p - bytes; + + line_start = p; + } + else + { + ++p; + } + } + + if (end_line) + return end_line - bytes; + } + break; + + + case eInputReaderGranularityAll: + { + // Nothing should be handle unless we see our end token + if (end_token) + { + size_t length = end_token - bytes; + size_t bytes_handled = m_callback (m_callback_baton, + *this, + eInputReaderGotToken, + bytes, + length); + m_done = true; + + p += bytes_handled + m_end_token.size(); + + // Consume any white space, such as newlines, beyond the end token + + while (p < end && isspace(*p)) + ++p; + + if (bytes_handled != length) + return bytes_handled; + else + { + return p - bytes; + //return bytes_handled + m_end_token.size(); + } + } + return 0; + } + break; + } + return 0; +} + +const char * +InputReader::GetPrompt () const +{ + if (!m_prompt.empty()) + return m_prompt.c_str(); + else + return NULL; +} + +void +InputReader::RefreshPrompt () +{ + if (m_debugger.GetCommandInterpreter().GetBatchCommandMode()) + return; + + if (!m_prompt.empty()) + { + File &out_file = m_debugger.GetOutputFile(); + if (out_file.IsValid()) + { + out_file.Printf ("%s", m_prompt.c_str()); + out_file.Flush(); + } + } +} + +void +InputReader::Notify (InputReaderAction notification) +{ + switch (notification) + { + case eInputReaderActivate: + case eInputReaderReactivate: + m_active = true; + m_reader_done.SetValue(false, eBroadcastAlways); + break; + + case eInputReaderDeactivate: + case eInputReaderDone: + m_active = false; + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + break; + + case eInputReaderGotToken: + return; // We don't notify the tokens here, it is done in HandleRawBytes + } + if (m_callback) + m_callback (m_callback_baton, *this, notification, NULL, 0); + if (notification == eInputReaderDone) + m_reader_done.SetValue(true, eBroadcastAlways); +} + +void +InputReader::WaitOnReaderIsDone () +{ + m_reader_done.WaitForValueEqualTo (true); +} + +const char * +InputReader::GranularityAsCString (lldb::InputReaderGranularity granularity) +{ + switch (granularity) + { + case eInputReaderGranularityInvalid: return "invalid"; + case eInputReaderGranularityByte: return "byte"; + case eInputReaderGranularityWord: return "word"; + case eInputReaderGranularityLine: return "line"; + case eInputReaderGranularityAll: return "all"; + } + + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "InputReaderGranularity = %i", granularity); + return unknown_state_string; +} + +bool +InputReader::HandlerData::GetBatchMode() +{ + return reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); +} + +lldb::StreamSP +InputReader::HandlerData::GetOutStream() +{ + return reader.GetDebugger().GetAsyncOutputStream(); +} diff --git a/source/Core/InputReaderEZ.cpp b/source/Core/InputReaderEZ.cpp new file mode 100644 index 00000000000..7a865bdc509 --- /dev/null +++ b/source/Core/InputReaderEZ.cpp @@ -0,0 +1,91 @@ +//===-- InputReaderEZ.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/InputReaderEZ.h" + +using namespace lldb; +using namespace lldb_private; + +size_t +InputReaderEZ::Callback_Impl(void *baton, + InputReader &reader, + lldb::InputReaderAction notification, + const char *bytes, + size_t bytes_len) + +{ + HandlerData hand_data(reader, + bytes, + bytes_len, + baton); + + switch (notification) + { + case eInputReaderActivate: + reader.ActivateHandler(hand_data); + break; + case eInputReaderDeactivate: + reader.DeactivateHandler(hand_data); + break; + case eInputReaderReactivate: + reader.ReactivateHandler(hand_data); + break; + case eInputReaderAsynchronousOutputWritten: + reader.AsynchronousOutputWrittenHandler(hand_data); + break; + case eInputReaderGotToken: + { + if (reader.GetSaveUserInput()) + reader.GetUserInput().AppendString(bytes, bytes_len); + reader.GotTokenHandler(hand_data); + } + break; + case eInputReaderInterrupt: + reader.InterruptHandler(hand_data); + break; + case eInputReaderEndOfFile: + reader.EOFHandler(hand_data); + break; + case eInputReaderDone: + reader.DoneHandler(hand_data); + break; + } + return bytes_len; +} + +Error +InputReaderEZ::Initialize(void* baton, + lldb::InputReaderGranularity token_size, + const char* end_token, + const char *prompt, + bool echo) +{ + return InputReader::Initialize(Callback_Impl, + baton, + token_size, + end_token, + prompt, + echo); +} + +Error +InputReaderEZ::Initialize(InitializationParameters& params) +{ + Error ret = Initialize(params.m_baton, + params.m_token_size, + params.m_end_token, + params.m_prompt, + params.m_echo); + m_save_user_input = params.m_save_user_input; + return ret; +} + +InputReaderEZ::~InputReaderEZ () +{ +} diff --git a/source/Core/InputReaderStack.cpp b/source/Core/InputReaderStack.cpp new file mode 100644 index 00000000000..764ea26550f --- /dev/null +++ b/source/Core/InputReaderStack.cpp @@ -0,0 +1,80 @@ +//===-- InputReaderStack.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/InputReaderStack.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + + +using namespace lldb; +using namespace lldb_private; + +InputReaderStack::InputReaderStack () : + m_input_readers (), + m_input_readers_mutex (Mutex::eMutexTypeRecursive) +{ +} + +InputReaderStack::~InputReaderStack () +{ +} + +size_t +InputReaderStack::GetSize () const +{ + Mutex::Locker locker (m_input_readers_mutex); + return m_input_readers.size(); +} + +void +InputReaderStack::Push (const lldb::InputReaderSP& reader_sp) +{ + if (reader_sp) + { + Mutex::Locker locker (m_input_readers_mutex); + m_input_readers.push (reader_sp); + } +} + +bool +InputReaderStack::IsEmpty () const +{ + Mutex::Locker locker (m_input_readers_mutex); + return m_input_readers.empty(); +} + +InputReaderSP +InputReaderStack::Top () +{ + InputReaderSP input_reader_sp; + { + Mutex::Locker locker (m_input_readers_mutex); + if (!m_input_readers.empty()) + input_reader_sp = m_input_readers.top(); + } + + return input_reader_sp; +} + +void +InputReaderStack::Pop () +{ + Mutex::Locker locker (m_input_readers_mutex); + if (!m_input_readers.empty()) + m_input_readers.pop(); +} + +Mutex & +InputReaderStack::GetStackMutex () +{ + return m_input_readers_mutex; +} diff --git a/source/Core/Language.cpp b/source/Core/Language.cpp new file mode 100644 index 00000000000..af62af37da0 --- /dev/null +++ b/source/Core/Language.cpp @@ -0,0 +1,151 @@ +//===-- Language.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" +#include "lldb/Core/Language.h" +#include "lldb/Core/Stream.h" +#include + +using namespace lldb; +using namespace lldb_private; + +#define ENUM_TO_DCSTREAM(x) case x: s->PutCString(#x); return + +struct LanguageStrings +{ + const char * names[3]; +}; + +static LanguageStrings +g_languages[] = +{ + { { "unknown" , NULL , NULL } }, + { { "c89" , NULL , "ISO C:1989" } }, + { { NULL , NULL , "K&R C" } }, + { { "ada83" , "Ada83" , "ISO Ada:1983" } }, + { { "c++" , "cxx" , "ISO C++:1998" } }, + { { "cobol74" , "Cobol74" , "ISO Cobol:1974" } }, + { { "cobol" , "Cobol85" , "ISO Cobol:1985." } }, + { { "f77" , "Fortran77" , "ISO Fortran 77." } }, + { { "f90" , "Fortran90" , "ISO Fortran 90" } }, + { { "pascal" , "Pascal83" , "ISO Pascal:1983" } }, + { { "modula2" , "Modula2" , "ISO Modula-2:1996" } }, + { { "java" , NULL , "Java" } }, + { { "c" , "C99" , "ISO C:1999" } }, + { { "ada" , "Ada95" , "ISO Ada:1995" } }, + { { "f95" , "Fortran95" , "ISO Fortran 95" } }, + { { "PLI" , NULL , "ANSI PL/I:1976" } }, + { { "objc" , NULL , "Objective-C" } }, + { { "objc++" , NULL , "Objective-C++" } }, + { { "upc" , NULL , "Unified Parallel C" } }, + { { "d" , NULL , "D" } }, + { { "python" , NULL , "Python" } } +}; + +static const size_t +g_num_languages = sizeof(g_languages)/sizeof(LanguageStrings); + +Language::Language(LanguageType language) : + m_language (language) +{ +} + +Language::~Language() +{ +} + +LanguageType +Language::GetLanguage() const +{ + return m_language; +} + +void +Language::Clear () +{ + m_language = eLanguageTypeUnknown; +} + +void +Language::SetLanguage(LanguageType language) +{ + m_language = language; +} + +bool +Language::SetLanguageFromCString(const char *language_cstr) +{ + size_t i, desc_idx; + const char *name; + + // First check the most common name for the languages + for (desc_idx=lldb::eDescriptionLevelBrief; desc_idxPutCString(lang_cstr); + else + s->Printf("Language(language = 0x%4.4x)", m_language); +} + + + + +Stream& +lldb_private::operator << (Stream& s, const Language& language) +{ + language.Dump(&s); + return s; +} + diff --git a/source/Core/Listener.cpp b/source/Core/Listener.cpp new file mode 100644 index 00000000000..aca2b374092 --- /dev/null +++ b/source/Core/Listener.cpp @@ -0,0 +1,557 @@ +//===-- Listener.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Listener.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Event.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/lldb-private-log.h" +#include + +using namespace lldb; +using namespace lldb_private; + +Listener::Listener(const char *name) : + m_name (name), + m_broadcasters(), + m_broadcasters_mutex (Mutex::eMutexTypeRecursive), + m_events (), + m_events_mutex (Mutex::eMutexTypeRecursive), + m_cond_wait() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf ("%p Listener::Listener('%s')", this, m_name.c_str()); +} + +Listener::~Listener() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + Mutex::Locker locker (m_broadcasters_mutex); + + size_t num_managers = m_broadcaster_managers.size(); + + for (size_t i = 0; i < num_managers; i++) + m_broadcaster_managers[i]->RemoveListener(*this); + + if (log) + log->Printf ("%p Listener::~Listener('%s')", this, m_name.c_str()); + Clear(); +} + +void +Listener::Clear() +{ + Mutex::Locker locker(m_broadcasters_mutex); + broadcaster_collection::iterator pos, end = m_broadcasters.end(); + for (pos = m_broadcasters.begin(); pos != end; ++pos) + pos->first->RemoveListener (this, pos->second.event_mask); + m_broadcasters.clear(); + m_cond_wait.SetValue (false, eBroadcastNever); + m_broadcasters.clear(); + Mutex::Locker event_locker(m_events_mutex); + m_events.clear(); +} + +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 + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.insert(std::make_pair(broadcaster, BroadcasterInfo(event_mask))); + } + + uint32_t acquired_mask = broadcaster->AddListener (this, event_mask); + + if (event_mask != acquired_mask) + { + + } + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener::StartListeningForEvents (broadcaster = %p, mask = 0x%8.8x) acquired_mask = 0x%8.8x for %s", + this, + 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 + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.insert(std::make_pair(broadcaster, BroadcasterInfo(event_mask, callback, callback_user_data))); + } + + uint32_t acquired_mask = broadcaster->AddListener (this, event_mask); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + if (log) + log->Printf ("%p Listener::StartListeningForEvents (broadcaster = %p, mask = 0x%8.8x, callback = %p, user_data = %p) acquired_mask = 0x%8.8x for %s", + this, broadcaster, event_mask, callback, 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" + { + Mutex::Locker locker(m_broadcasters_mutex); + m_broadcasters.erase (broadcaster); + } + // Remove the broadcaster from our set of broadcasters + return broadcaster->RemoveListener (this, event_mask); + } + + return false; +} + +// Called when a Broadcaster is in its destuctor. 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" + { + Mutex::Locker broadcasters_locker(m_broadcasters_mutex); + m_broadcasters.erase (broadcaster); + } + + // Scope for "event_locker" + { + Mutex::Locker event_locker(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; + } + + if (m_events.empty()) + m_cond_wait.SetValue (false, eBroadcastNever); + + } +} + +void +Listener::BroadcasterManagerWillDestruct (BroadcasterManager *manager) +{ + // Just need to remove this broadcast manager from the list of managers: + broadcaster_manager_collection::iterator iter, end_iter = m_broadcaster_managers.end(); + iter = find(m_broadcaster_managers.begin(), end_iter, manager); + 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) + log->Printf ("%p Listener('%s')::AddEvent (event_sp = {%p})", this, m_name.c_str(), event_sp.get()); + + // Scope for "locker" + { + Mutex::Locker locker(m_events_mutex); + m_events.push_back (event_sp); + } + m_cond_wait.SetValue (true, eBroadcastAlways); +} + +class EventBroadcasterMatches +{ +public: + EventBroadcasterMatches (Broadcaster *broadcaster) : + m_broadcaster (broadcaster) { + } + + bool operator() (const EventSP &event_sp) const + { + if (event_sp->BroadcasterIs(m_broadcaster)) + return true; + else + return false; + } + +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; + const ConstString &event_broadcaster_name = event_sp->GetBroadcaster()->GetBroadcasterName(); + for (uint32_t i=0; iGetType()) + return true; + return false; + } + +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 +( + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp, + bool remove) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + + Mutex::Locker lock(m_events_mutex); + + if (m_events.empty()) + return false; + + + Listener::event_collection::iterator pos = m_events.end(); + + if (broadcaster == NULL && broadcaster_names == NULL && 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) + log->Printf ("%p '%s' Listener::FindNextEventInternal(broadcaster=%p, broadcaster_names=%p[%u], event_type_mask=0x%8.8x, remove=%i) event %p", + this, + GetName(), + broadcaster, + broadcaster_names, + num_broadcaster_names, + event_type_mask, + remove, + event_sp.get()); + + if (remove) + { + m_events.erase(pos); + + if (m_events.empty()) + m_cond_wait.SetValue (false, eBroadcastNever); + } + + // 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(); + + // Don't call DoOnRemoval if you aren't removing the event... + if (remove) + event_sp->DoOnRemoval(); + + return true; + } + + event_sp.reset(); + return false; +} + +Event * +Listener::PeekAtNextEvent () +{ + EventSP event_sp; + if (FindNextEventInternal (NULL, NULL, 0, 0, event_sp, false)) + return event_sp.get(); + return NULL; +} + +Event * +Listener::PeekAtNextEventForBroadcaster (Broadcaster *broadcaster) +{ + EventSP event_sp; + if (FindNextEventInternal (broadcaster, NULL, 0, 0, event_sp, false)) + return event_sp.get(); + return NULL; +} + +Event * +Listener::PeekAtNextEventForBroadcasterWithType (Broadcaster *broadcaster, uint32_t event_type_mask) +{ + EventSP event_sp; + if (FindNextEventInternal (broadcaster, NULL, 0, event_type_mask, event_sp, false)) + return event_sp.get(); + return NULL; +} + + +bool +Listener::GetNextEventInternal +( + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + return FindNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp, true); +} + +bool +Listener::GetNextEvent (EventSP &event_sp) +{ + return GetNextEventInternal (NULL, NULL, 0, 0, event_sp); +} + + +bool +Listener::GetNextEventForBroadcaster (Broadcaster *broadcaster, EventSP &event_sp) +{ + return GetNextEventInternal (broadcaster, NULL, 0, 0, event_sp); +} + +bool +Listener::GetNextEventForBroadcasterWithType (Broadcaster *broadcaster, uint32_t event_type_mask, EventSP &event_sp) +{ + return GetNextEventInternal (broadcaster, NULL, 0, event_type_mask, event_sp); +} + + +bool +Listener::WaitForEventsInternal +( + const TimeValue *timeout, + Broadcaster *broadcaster, // NULL for any broadcaster + const ConstString *broadcaster_names, // NULL for any event + uint32_t num_broadcaster_names, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS)); + bool timed_out = false; + + if (log) + { + log->Printf ("%p Listener::WaitForEventsInternal (timeout = { %p }) for %s", + this, timeout, m_name.c_str()); + } + + while (1) + { + // Note, we don't want to lock the m_events_mutex in the call to GetNextEventInternal, since the DoOnRemoval + // code might require that new events be serviced. For instance, the Breakpoint Command's + if (GetNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp)) + return true; + + { + // Reset condition value to false, so we can wait for new events to be + // added that might meet our current filter + // But first poll for any new event that might satisfy our condition, and if so consume it, + // otherwise wait. + + Mutex::Locker event_locker(m_events_mutex); + const bool remove = false; + if (FindNextEventInternal (broadcaster, broadcaster_names, num_broadcaster_names, event_type_mask, event_sp, remove)) + continue; + else + m_cond_wait.SetValue (false, eBroadcastNever); + } + + if (m_cond_wait.WaitForValueEqualTo (true, timeout, &timed_out)) + continue; + + else if (timed_out) + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + log->Printf ("%p Listener::WaitForEventsInternal() timed out for %s", this, m_name.c_str()); + break; + } + else + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EVENTS); + if (log) + log->Printf ("%p Listener::WaitForEventsInternal() unknown error for %s", this, m_name.c_str()); + break; + } + } + + return false; +} + +bool +Listener::WaitForEventForBroadcasterWithType +( + const TimeValue *timeout, + Broadcaster *broadcaster, + uint32_t event_type_mask, + EventSP &event_sp +) +{ + return WaitForEventsInternal (timeout, broadcaster, NULL, 0, event_type_mask, event_sp); +} + +bool +Listener::WaitForEventForBroadcaster +( + const TimeValue *timeout, + Broadcaster *broadcaster, + EventSP &event_sp +) +{ + return WaitForEventsInternal (timeout, broadcaster, NULL, 0, 0, event_sp); +} + +bool +Listener::WaitForEvent (const TimeValue *timeout, EventSP &event_sp) +{ + return WaitForEventsInternal (timeout, NULL, NULL, 0, 0, event_sp); +} + +//Listener::broadcaster_collection::iterator +//Listener::FindBroadcasterWithMask (Broadcaster *broadcaster, uint32_t event_mask, bool exact) +//{ +// broadcaster_collection::iterator pos; +// broadcaster_collection::iterator end = m_broadcasters.end(); +// for (pos = m_broadcasters.find (broadcaster); +// pos != end && pos->first == broadcaster; +// ++pos) +// { +// if (exact) +// { +// if ((event_mask & pos->second.event_mask) == event_mask) +// return pos; +// } +// else +// { +// if (event_mask & pos->second.event_mask) +// return pos; +// } +// } +// return end; +//} + +size_t +Listener::HandleBroadcastEvent (EventSP &event_sp) +{ + size_t num_handled = 0; + Mutex::Locker locker(m_broadcasters_mutex); + Broadcaster *broadcaster = event_sp->GetBroadcaster(); + broadcaster_collection::iterator pos; + broadcaster_collection::iterator end = m_broadcasters.end(); + for (pos = m_broadcasters.find (broadcaster); + pos != end && pos->first == broadcaster; + ++pos) + { + BroadcasterInfo info = pos->second; + if (event_sp->GetType () & info.event_mask) + { + if (info.callback != NULL) + { + info.callback (event_sp, info.callback_user_data); + ++num_handled; + } + } + } + return num_handled; +} + +uint32_t +Listener::StartListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec) +{ + // The BroadcasterManager mutex must be locked before m_broadcasters_mutex + // to avoid violating the lock hierarchy (manager before broadcasters). + Mutex::Locker manager_locker(manager.m_manager_mutex); + Mutex::Locker locker(m_broadcasters_mutex); + + uint32_t bits_acquired = manager.RegisterListenerForEvents(*this, event_spec); + if (bits_acquired) + m_broadcaster_managers.push_back(&manager); + + return bits_acquired; +} + +bool +Listener::StopListeningForEventSpec (BroadcasterManager &manager, + const BroadcastEventSpec &event_spec) +{ + Mutex::Locker locker(m_broadcasters_mutex); + return manager.UnregisterListenerForEvents (*this, event_spec); + +} + + diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp new file mode 100644 index 00000000000..d73ab15f697 --- /dev/null +++ b/source/Core/Log.cpp @@ -0,0 +1,529 @@ +//===-- Log.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include +#include +#include +#include + +// C++ Includes +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/TimeValue.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/Args.h" +using namespace lldb; +using namespace lldb_private; + +Log::Log () : + m_stream_sp(), + m_options(0), + m_mask_bits(0) +{ +} + +Log::Log (const StreamSP &stream_sp) : + m_stream_sp(stream_sp), + m_options(0), + m_mask_bits(0) +{ +} + +Log::~Log () +{ +} + +Flags & +Log::GetOptions() +{ + return m_options; +} + +const Flags & +Log::GetOptions() const +{ + return m_options; +} + +Flags & +Log::GetMask() +{ + return m_mask_bits; +} + +const Flags & +Log::GetMask() const +{ + return m_mask_bits; +} + + +//---------------------------------------------------------------------- +// 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::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) +{ + if (m_stream_sp) + { + static uint32_t g_sequence_id = 0; + StreamString header; + // Enabling the thread safe logging actually deadlocks right now. + // Need to fix this at some point. +// static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); +// Mutex::Locker locker (g_LogThreadedMutex); + + // Add a sequence ID if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_SEQUENCE)) + header.Printf ("%u ", ++g_sequence_id); + + // Timestamp if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) + { + struct timeval tv = TimeValue::Now().GetAsTimeVal(); + header.Printf ("%9ld.%6.6d ", tv.tv_sec, (int32_t)tv.tv_usec); + } + + // Add the process and thread if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_PROC_AND_THREAD)) + header.Printf ("[%4.4x/%4.4" PRIx64 "]: ", getpid(), Host::GetCurrentThreadID()); + + // Add the process and thread if requested + if (m_options.Test (LLDB_LOG_OPTION_PREPEND_THREAD_NAME)) + { + std::string thread_name (Host::GetThreadName (getpid(), Host::GetCurrentThreadID())); + if (!thread_name.empty()) + header.Printf ("%s ", thread_name.c_str()); + } + + header.PrintfVarArg (format, args); + m_stream_sp->Printf("%s\n", header.GetData()); + + if (m_options.Test (LLDB_LOG_OPTION_BACKTRACE)) + Host::Backtrace (*m_stream_sp, 1024); + m_stream_sp->Flush(); + } +} + + +void +Log::PutCString (const char *cstr) +{ + Printf ("%s", cstr); +} + + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::Printf(const char *format, ...) +{ + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (0, format, args); + va_end (args); +} + +void +Log::VAPrintf (const char *format, va_list args) +{ + PrintfWithFlagsVarArg (0, format, args); +} + + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::PrintfWithFlags (uint32_t flags, const char *format, ...) +{ + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (flags, format, args); + va_end (args); +} + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global debug option is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +Log::Debug (const char *format, ...) +{ + if (GetOptions().Test(LLDB_LOG_OPTION_DEBUG)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Print debug strings if and only if the global debug option is set to +// a non-zero value. +//---------------------------------------------------------------------- +void +Log::DebugVerbose (const char *format, ...) +{ + if (GetOptions().AllSet (LLDB_LOG_OPTION_DEBUG | LLDB_LOG_OPTION_VERBOSE)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG | LLDB_LOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Log only if all of the bits are set +//---------------------------------------------------------------------- +void +Log::LogIf (uint32_t bits, const char *format, ...) +{ + if (m_options.AllSet (bits)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (0, format, args); + va_end (args); + } +} + + +//---------------------------------------------------------------------- +// Printing of errors that are not fatal. +//---------------------------------------------------------------------- +void +Log::Error (const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_ERROR, "error: %s", arg_msg); + free (arg_msg); + } +} + +//---------------------------------------------------------------------- +// Printing of errors that ARE fatal. Exit with ERR exit code +// immediately. +//---------------------------------------------------------------------- +void +Log::FatalError (int err, const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_ERROR | LLDB_LOG_FLAG_FATAL, "error: %s", arg_msg); + ::free (arg_msg); + } + ::exit (err); +} + + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +Log::Verbose (const char *format, ...) +{ + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + { + va_list args; + va_start (args, format); + PrintfWithFlagsVarArg (LLDB_LOG_FLAG_VERBOSE, format, args); + va_end (args); + } +} + +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal only if verbose mode is +// enabled. +//---------------------------------------------------------------------- +void +Log::WarningVerbose (const char *format, ...) +{ + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + { + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_WARNING | LLDB_LOG_FLAG_VERBOSE, "warning: %s", arg_msg); + free (arg_msg); + } + } +} +//---------------------------------------------------------------------- +// Printing of warnings that are not fatal. +//---------------------------------------------------------------------- +void +Log::Warning (const char *format, ...) +{ + char *arg_msg = NULL; + va_list args; + va_start (args, format); + ::vasprintf (&arg_msg, format, args); + va_end (args); + + if (arg_msg != NULL) + { + PrintfWithFlags (LLDB_LOG_FLAG_WARNING, "warning: %s", arg_msg); + free (arg_msg); + } +} + +typedef std::map CallbackMap; +typedef CallbackMap::iterator CallbackMapIter; + +typedef std::map LogChannelMap; +typedef LogChannelMap::iterator LogChannelMapIter; + + +// Surround our callback map with a singleton function so we don't have any +// global initializers. +static CallbackMap & +GetCallbackMap () +{ + static CallbackMap g_callback_map; + return g_callback_map; +} + +static LogChannelMap & +GetChannelMap () +{ + static LogChannelMap g_channel_map; + return g_channel_map; +} + +void +Log::RegisterLogChannel (const ConstString &channel, const Log::Callbacks &log_callbacks) +{ + GetCallbackMap().insert(std::make_pair(channel, log_callbacks)); +} + +bool +Log::UnregisterLogChannel (const ConstString &channel) +{ + return GetCallbackMap().erase(channel) != 0; +} + +bool +Log::GetLogChannelCallbacks (const ConstString &channel, Log::Callbacks &log_callbacks) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos = callback_map.find(channel); + if (pos != callback_map.end()) + { + log_callbacks = pos->second; + return true; + } + ::memset (&log_callbacks, 0, sizeof(log_callbacks)); + return false; +} + +void +Log::EnableAllLogChannels +( + StreamSP &log_stream_sp, + uint32_t log_options, + const char **categories, + Stream *feedback_strm +) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.enable (log_stream_sp, log_options, categories, feedback_strm); + + LogChannelMap &channel_map = GetChannelMap (); + LogChannelMapIter channel_pos, channel_end = channel_map.end(); + for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos) + { + channel_pos->second->Enable (log_stream_sp, log_options, feedback_strm, categories); + } + +} + +void +Log::AutoCompleteChannelName (const char *channel_name, StringList &matches) +{ + LogChannelMap &map = GetChannelMap (); + LogChannelMapIter pos, end = map.end(); + for (pos = map.begin(); pos != end; ++pos) + { + const char *pos_channel_name = pos->first.GetCString(); + if (channel_name && channel_name[0]) + { + if (NameMatches (channel_name, eNameMatchStartsWith, pos_channel_name)) + { + matches.AppendString(pos_channel_name); + } + } + else + matches.AppendString(pos_channel_name); + + } +} + +void +Log::DisableAllLogChannels (Stream *feedback_strm) +{ + CallbackMap &callback_map = GetCallbackMap (); + CallbackMapIter pos, end = callback_map.end(); + const char *categories[1] = {NULL}; + + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.disable (categories, feedback_strm); + + LogChannelMap &channel_map = GetChannelMap (); + LogChannelMapIter channel_pos, channel_end = channel_map.end(); + for (channel_pos = channel_map.begin(); channel_pos != channel_end; ++channel_pos) + channel_pos->second->Disable (categories, feedback_strm); +} + +void +Log::Initialize() +{ + Log::Callbacks log_callbacks = { DisableLog, EnableLog, ListLogCategories }; + Log::RegisterLogChannel (ConstString("lldb"), log_callbacks); +} + +void +Log::Terminate () +{ + DisableAllLogChannels (NULL); +} + +void +Log::ListAllLogChannels (Stream *strm) +{ + CallbackMap &callback_map = GetCallbackMap (); + LogChannelMap &channel_map = GetChannelMap (); + + if (callback_map.empty() && channel_map.empty()) + { + strm->PutCString ("No logging channels are currently registered.\n"); + return; + } + + CallbackMapIter pos, end = callback_map.end(); + for (pos = callback_map.begin(); pos != end; ++pos) + pos->second.list_categories (strm); + + uint32_t idx = 0; + const char *name; + for (idx = 0; (name = PluginManager::GetLogChannelCreateNameAtIndex (idx)) != NULL; ++idx) + { + LogChannelSP log_channel_sp(LogChannel::FindPlugin (name)); + if (log_channel_sp) + log_channel_sp->ListCategories (strm); + } +} + +bool +Log::GetVerbose() const +{ + // FIXME: This has to be centralized between the stream and the log... + if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + return true; + + if (m_stream_sp) + return m_stream_sp->GetVerbose(); + return false; +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Log::GetDebug() const +{ + if (m_stream_sp) + return m_stream_sp->GetDebug(); + return false; +} + + +LogChannelSP +LogChannel::FindPlugin (const char *plugin_name) +{ + LogChannelSP log_channel_sp; + LogChannelMap &channel_map = GetChannelMap (); + ConstString log_channel_name (plugin_name); + LogChannelMapIter pos = channel_map.find (log_channel_name); + if (pos == channel_map.end()) + { + ConstString const_plugin_name (plugin_name); + LogChannelCreateInstance create_callback = PluginManager::GetLogChannelCreateCallbackForPluginName (const_plugin_name); + if (create_callback) + { + log_channel_sp.reset(create_callback()); + if (log_channel_sp) + { + // Cache the one and only loaded instance of each log channel + // plug-in after it has been loaded once. + channel_map[log_channel_name] = log_channel_sp; + } + } + } + else + { + // We have already loaded an instance of this log channel class, + // so just return the cached instance. + log_channel_sp = pos->second; + } + return log_channel_sp; +} + +LogChannel::LogChannel () : + m_log_ap () +{ +} + +LogChannel::~LogChannel () +{ +} + + diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp new file mode 100644 index 00000000000..4655eb131a6 --- /dev/null +++ b/source/Core/Mangled.cpp @@ -0,0 +1,313 @@ +//===-- Mangled.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// FreeBSD9-STABLE requires this to know about size_t in cxxabi.h +#include +#include + + +#include "llvm/ADT/DenseMap.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Mangled.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" +#include +#include +#include + +using namespace lldb_private; + +static inline bool +cstring_is_mangled (const char *s) +{ + if (s) + return s[0] == '_' && s[1] == 'Z'; + return false; +} + +#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 (const ConstString &s, bool mangled) : + m_mangled(), + m_demangled() +{ + if (s) + SetValue(s, mangled); +} + +Mangled::Mangled (const ConstString &s) : + m_mangled(), + m_demangled() +{ + if (s) + SetValue(s); +} + +//---------------------------------------------------------------------- +// 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(this) : NULL; +} + +//---------------------------------------------------------------------- +// 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 the string values. +//---------------------------------------------------------------------- +int +Mangled::Compare (const Mangled& a, const Mangled& b) +{ + return ConstString::Compare(a.GetName(ePreferMangled), a.GetName(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 (const 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 (const 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(); + } +} + + +//---------------------------------------------------------------------- +// 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. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetDemangledName () 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) + { + // We need to generate and cache the demangled name. + Timer scoped_timer (__PRETTY_FUNCTION__, + "Mangled::GetDemangledName (m_mangled = %s)", + m_mangled.GetCString()); + + // Don't bother running anything that isn't mangled + const char *mangled_cstr = m_mangled.GetCString(); + if (cstring_is_mangled(mangled_cstr)) + { + if (!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 = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL); + + if (demangled_name) + { + m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); + free (demangled_name); + } + } + } + if (!m_demangled) + { + // Set the demangled string to the empty string to indicate we + // tried to parse it once and failed. + m_demangled.SetCString(""); + } + } + + return m_demangled; +} + + +bool +Mangled::NameMatches (const RegularExpression& regex) const +{ + if (m_mangled && regex.Execute (m_mangled.AsCString())) + return true; + + if (GetDemangledName() && regex.Execute (m_demangled.AsCString())) + return true; + return false; +} + +//---------------------------------------------------------------------- +// Get the demangled name if there is one, else return the mangled name. +//---------------------------------------------------------------------- +const ConstString& +Mangled::GetName (Mangled::NamePreference preference) const +{ + if (preference == ePreferDemangled) + { + // Call the accessor to make sure we get a demangled name in case + // it hasn't been demangled yet... + if (GetDemangledName()) + return m_demangled; + return m_mangled; + } + else + { + if (m_mangled) + return m_mangled; + return GetDemangledName(); + } +} + +//---------------------------------------------------------------------- +// 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 : ""); + } +} + +//---------------------------------------------------------------------- +// 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 = ", (int)sizeof(void*) * 2, 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(); +} + +//---------------------------------------------------------------------- +// Dump OBJ to the supplied stream S. +//---------------------------------------------------------------------- +Stream& +operator << (Stream& s, const Mangled& obj) +{ + if (obj.GetMangledName()) + s << "mangled = '" << obj.GetMangledName() << "'"; + + const ConstString& demangled = obj.GetDemangledName(); + if (demangled) + s << ", demangled = '" << demangled << '\''; + else + s << ", demangled = "; + return s; +} diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp new file mode 100644 index 00000000000..4252ed4cb6c --- /dev/null +++ b/source/Core/Module.cpp @@ -0,0 +1,1609 @@ +//===-- Module.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Symbols.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/CPPLanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" +#include "lldb/Symbol/SymbolFile.h" + +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 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 = NULL; + if (g_module_collection == NULL) + g_module_collection = new ModuleCollection(); + + return *g_module_collection; +} + +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 Mutex *g_module_collection_mutex = NULL; + if (g_module_collection_mutex == NULL) + g_module_collection_mutex = new Mutex (Mutex::eMutexTypeRecursive); // NOTE: known leak + return g_module_collection_mutex; +} + +size_t +Module::GetNumberAllocatedModules () +{ + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + return GetModuleCollection().size(); +} + +Module * +Module::GetAllocatedModuleAtIndex (size_t idx) +{ + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + if (idx < modules.size()) + return modules[idx]; + return NULL; +} +#if 0 + +// These functions help us to determine if modules are still loaded, yet don't require that +// you have a command interpreter and can easily be called from an external debugger. +namespace lldb { + + void + ClearModuleInfo (void) + { + const bool mandatory = true; + ModuleList::RemoveOrphanSharedModules(mandatory); + } + + void + DumpModuleInfo (void) + { + Mutex::Locker locker (Module::GetAllocationModuleCollectionMutex()); + ModuleCollection &modules = GetModuleCollection(); + const size_t count = modules.size(); + printf ("%s: %" PRIu64 " modules:\n", __PRETTY_FUNCTION__, (uint64_t)count); + for (size_t i=0; iGetDescription(&strm, eDescriptionLevelFull); + printf ("%p: shared = %i, ref_count = %3u, module = %s\n", + module, + in_shared_module_list, + (uint32_t)module->use_count(), + strm.GetString().c_str()); + } + } +} + +#endif + +Module::Module (const ModuleSpec &module_spec) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (module_spec.GetFileSpec().GetModificationTime()), + m_arch (module_spec.GetArchitecture()), + m_uuid (), + m_file (module_spec.GetFileSpec()), + m_platform_file(module_spec.GetPlatformFileSpec()), + m_symfile_spec (module_spec.GetSymbolFileSpec()), + m_object_name (module_spec.GetObjectName()), + m_object_offset (module_spec.GetObjectOffset()), + m_object_mod_time (module_spec.GetObjectModificationTime()), + m_objfile_sp (), + m_symfile_ap (), + m_ast (), + m_source_mappings (), + m_did_load_objfile (false), + m_did_load_symbol_vendor (false), + m_did_parse_uuid (false), + m_did_init_ast (false), + m_is_dynamic_loader_module (false), + m_file_has_changed (false), + m_first_file_changed_log (false) +{ + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + GetModuleCollection().push_back(this); + } + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::Module((%s) '%s%s%s%s')", + 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(const FileSpec& file_spec, + const ArchSpec& arch, + const ConstString *object_name, + off_t object_offset, + const TimeValue *object_mod_time_ptr) : + m_mutex (Mutex::eMutexTypeRecursive), + m_mod_time (file_spec.GetModificationTime()), + m_arch (arch), + m_uuid (), + m_file (file_spec), + m_platform_file(), + m_symfile_spec (), + m_object_name (), + m_object_offset (object_offset), + m_object_mod_time (), + m_objfile_sp (), + m_symfile_ap (), + m_ast (), + m_source_mappings (), + m_did_load_objfile (false), + m_did_load_symbol_vendor (false), + m_did_parse_uuid (false), + m_did_init_ast (false), + m_is_dynamic_loader_module (false), + m_file_has_changed (false), + m_first_file_changed_log (false) +{ + // Scope for locker below... + { + Mutex::Locker locker (GetAllocationModuleCollectionMutex()); + GetModuleCollection().push_back(this); + } + + if (object_name) + m_object_name = *object_name; + + if (object_mod_time_ptr) + m_object_mod_time = *object_mod_time_ptr; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_OBJECT|LIBLLDB_LOG_MODULES)); + if (log) + log->Printf ("%p Module::Module((%s) '%s%s%s%s')", + 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() +{ + // 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 + Mutex::Locker locker (m_mutex); + // Scope for locker below... + { + Mutex::Locker locker (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) + log->Printf ("%p Module::~Module((%s) '%s%s%s%s')", + 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_ap.reset(); + m_symfile_ap.reset(); + m_objfile_sp.reset(); +} + +ObjectFile * +Module::GetMemoryObjectFile (const lldb::ProcessSP &process_sp, lldb::addr_t header_addr, Error &error) +{ + if (m_objfile_sp) + { + error.SetErrorString ("object file already exists"); + } + else + { + Mutex::Locker locker (m_mutex); + if (process_sp) + { + m_did_load_objfile = true; + std::unique_ptr data_ap (new DataBufferHeap (512, 0)); + Error readmem_error; + const size_t bytes_read = process_sp->ReadMemory (header_addr, + data_ap->GetBytes(), + data_ap->GetByteSize(), + readmem_error); + if (bytes_read == 512) + { + DataBufferSP data_sp(data_ap.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.SetCString (s.GetData()); + + // 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_objfile_sp->GetArchitecture (m_arch); + } + 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() +{ + Mutex::Locker locker (m_mutex); + if (m_did_parse_uuid == false) + { + ObjectFile * obj_file = GetObjectFile (); + + if (obj_file != NULL) + { + obj_file->GetUUID(&m_uuid); + m_did_parse_uuid = true; + } + } + return m_uuid; +} + +ClangASTContext & +Module::GetClangASTContext () +{ + Mutex::Locker locker (m_mutex); + if (m_did_init_ast == false) + { + ObjectFile * objfile = GetObjectFile(); + ArchSpec object_arch; + if (objfile && objfile->GetArchitecture(object_arch)) + { + m_did_init_ast = true; + + // 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 (object_arch.GetTriple().getVendor() == llvm::Triple::Apple + && object_arch.GetTriple().getOS() == llvm::Triple::UnknownOS) + { + if (object_arch.GetTriple().getArch() == llvm::Triple::arm || + object_arch.GetTriple().getArch() == llvm::Triple::thumb) + { + object_arch.GetTriple().setOS(llvm::Triple::IOS); + } + else + { + object_arch.GetTriple().setOS(llvm::Triple::MacOSX); + } + } + m_ast.SetArchitecture (object_arch); + } + } + return m_ast; +} + +void +Module::ParseAllDebugSymbols() +{ + Mutex::Locker locker (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) + { + sc.function = NULL; + symbols->ParseVariablesForContext(sc); + + symbols->ParseCompileUnitFunctions(sc); + + for (size_t func_idx = 0; (sc.function = sc.comp_unit->GetFunctionAtIndex(func_idx).get()) != NULL; ++func_idx) + { + symbols->ParseFunctionBlocks(sc); + + // Parse the variables for this function and all its blocks + symbols->ParseVariablesForContext(sc); + } + + + // Parse all types for this compile unit + sc.function = NULL; + symbols->ParseTypes(sc); + } + } +} + +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}", this); +} + +size_t +Module::GetNumCompileUnits() +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, "Module::GetNumCompileUnits (module = %p)", this); + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->GetNumCompileUnits(); + return 0; +} + +CompUnitSP +Module::GetCompileUnitAtIndex (size_t index) +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, "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, uint32_t resolve_scope, SymbolContext& sc) +{ + Mutex::Locker locker (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; + + // 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 ) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + resolved_flags |= symbols->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)) + { + SymbolVendor* sym_vendor = GetSymbolVendor(); + if (sym_vendor) + { + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab) + { + if (so_addr.IsSectionOffset()) + { + sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + if (sc.symbol) + resolved_flags |= eSymbolContextSymbol; + } + } + } + } + } + return resolved_flags; +} + +uint32_t +Module::ResolveSymbolContextForFilePath +( + const char *file_path, + uint32_t line, + bool check_inlines, + uint32_t resolve_scope, + SymbolContextList& sc_list +) +{ + FileSpec file_spec(file_path, false); + 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, uint32_t resolve_scope, SymbolContextList& sc_list) +{ + Mutex::Locker locker (m_mutex); + Timer scoped_timer(__PRETTY_FUNCTION__, + "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 (const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(name, namespace_decl, append, max_matches, variables); + return 0; +} + +size_t +Module::FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variables) +{ + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindGlobalVariables(regex, append, 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 = path.GetDirectory(); + for (size_t i=0; iFindFunctions(lookup_name, + namespace_decl, + lookup_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(lookup_name, lookup_name_type_mask, sc_list); + } + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (iFindFunctions(name, namespace_decl, 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 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; iSymbolAtIndex(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 FileAddrToIndexMap; + FileAddrToIndexMap file_addr_to_index; + for (size_t i=start_size; iGetAddressRange().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; iSymbolAtIndex(symbol_indexes[i]); + SymbolType sym_type = sc.symbol->GetType(); + if (sc.symbol && (sym_type == eSymbolTypeCode || + sym_type == eSymbolTypeResolver)) + { + FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find(sc.symbol->GetAddress().GetFileAddress()); + if (pos == end) + sc_list.Append(sc); + else + sc_list[pos->second].symbol = sc.symbol; + } + } + } + } + } + } + } + return sc_list.GetSize() - start_size; +} + +size_t +Module::FindTypes_Impl (const SymbolContext& sc, + const ConstString &name, + const ClangNamespaceDecl *namespace_decl, + bool append, + size_t max_matches, + TypeList& types) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + if (sc.module_sp.get() == NULL || sc.module_sp.get() == this) + { + SymbolVendor *symbols = GetSymbolVendor (); + if (symbols) + return symbols->FindTypes(sc, name, namespace_decl, append, max_matches, types); + } + return 0; +} + +size_t +Module::FindTypesInNamespace (const SymbolContext& sc, + const ConstString &type_name, + const ClangNamespaceDecl *namespace_decl, + size_t max_matches, + TypeList& type_list) +{ + const bool append = true; + return FindTypes_Impl(sc, type_name, namespace_decl, append, max_matches, type_list); +} + +lldb::TypeSP +Module::FindFirstType (const SymbolContext& sc, + const ConstString &name, + bool exact_match) +{ + TypeList type_list; + const size_t num_matches = FindTypes (sc, name, exact_match, 1, type_list); + if (num_matches) + return type_list.GetTypeAtIndex(0); + return TypeSP(); +} + + +size_t +Module::FindTypes (const SymbolContext& sc, + const ConstString &name, + bool exact_match, + size_t max_matches, + TypeList& types) +{ + size_t num_matches = 0; + const char *type_name_cstr = name.GetCString(); + std::string type_scope; + std::string type_basename; + const bool append = true; + TypeClass type_class = eTypeClassAny; + 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 qualfied names to match + + if (type_scope.size() >= 2 && type_scope[0] == ':' && type_scope[1] == ':') + { + type_scope.erase(0,2); + exact_match = true; + } + ConstString type_basename_const_str (type_basename.c_str()); + if (FindTypes_Impl(sc, type_basename_const_str, NULL, append, max_matches, types)) + { + types.RemoveMismatchedTypes (type_scope, type_basename, type_class, exact_match); + num_matches = types.GetSize(); + } + } + else + { + // The type is not in a namespace/class scope, just search for it by basename + if (type_class != eTypeClassAny) + { + // The "type_name_cstr" will have been modified if we have a valid type class + // prefix (like "struct", "class", "union", "typedef" etc). + num_matches = FindTypes_Impl(sc, ConstString(type_name_cstr), NULL, append, max_matches, types); + types.RemoveMismatchedTypes (type_class); + num_matches = types.GetSize(); + } + else + { + num_matches = FindTypes_Impl(sc, name, NULL, append, max_matches, types); + } + } + + return num_matches; + +} + +SymbolVendor* +Module::GetSymbolVendor (bool can_create, lldb_private::Stream *feedback_strm) +{ + Mutex::Locker locker (m_mutex); + if (m_did_load_symbol_vendor == false && can_create) + { + ObjectFile *obj_file = GetObjectFile (); + if (obj_file != NULL) + { + Timer scoped_timer(__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + m_symfile_ap.reset(SymbolVendor::FindPlugin(shared_from_this(), feedback_strm)); + m_did_load_symbol_vendor = true; + } + } + return m_symfile_ap.get(); +} + +void +Module::SetFileSpecAndObjectName (const FileSpec &file, const 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 = file.GetModificationTime(); + 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) +{ + Mutex::Locker locker (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.GetString().c_str()); + + } +} + +bool +Module::FileHasChanged () const +{ + if (m_file_has_changed == false) + m_file_has_changed = (m_file.GetModificationTime() != m_mod_time); + return m_file_has_changed; +} + +void +Module::ReportErrorIfModifyDetected (const char *format, ...) +{ + if (m_first_file_changed_log == false) + { + 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.GetString().c_str()); + } + } + } +} + +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.GetString().c_str()); + } +} + +void +Module::LogMessage (Log *log, const char *format, ...) +{ + if (log) + { + 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.GetString().c_str()); + } +} + +void +Module::LogMessageVerboseBacktrace (Log *log, const char *format, ...) +{ + if (log) + { + 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()) + Host::Backtrace (log_message, 1024); + log->PutCString(log_message.GetString().c_str()); + } +} + +void +Module::Dump(Stream *s) +{ + Mutex::Locker locker (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 NULL; +} + +const ConstString & +Module::GetObjectName() const +{ + return m_object_name; +} + +ObjectFile * +Module::GetObjectFile() +{ + Mutex::Locker locker (m_mutex); + if (m_did_load_objfile == false) + { + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); + DataBufferSP data_sp; + lldb::offset_t data_offset = 0; + const lldb::offset_t file_size = m_file.GetByteSize(); + 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. + m_objfile_sp->GetArchitecture (m_arch); + } + } + } + return m_objfile_sp.get(); +} + +SectionList * +Module::GetSectionList() +{ + // Populate m_unified_sections_ap with sections from objfile. + if (m_sections_ap.get() == NULL) + { + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + obj_file->CreateSections(*GetUnifiedSectionList()); + } + return m_sections_ap.get(); +} + +SectionList * +Module::GetUnifiedSectionList() +{ + // Populate m_unified_sections_ap with sections from objfile. + if (m_sections_ap.get() == NULL) + m_sections_ap.reset(new SectionList()); + return m_sections_ap.get(); +} + +const Symbol * +Module::FindFirstSymbolWithNameAndType (const ConstString &name, SymbolType symbol_type) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "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 NULL; +} +void +Module::SymbolIndicesToSymbolContextList (Symtab *symtab, std::vector &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 (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "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 (const 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. + + + Timer scoped_timer(__PRETTY_FUNCTION__, + "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 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 ®ex, SymbolType symbol_type, SymbolContextList &sc_list) +{ + // No need to protect this call using m_mutex all other method calls are + // already thread safe. + + Timer scoped_timer(__PRETTY_FUNCTION__, + "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)", + regex.GetText(), + 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 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::SetSymbolFileFileSpec (const FileSpec &file) +{ + // Remove any sections in the unified section list that come from the current symbol vendor. + if (m_symfile_ap) + { + SectionList *section_list = GetSectionList(); + SymbolFile *symbol_file = m_symfile_ap->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 && 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); + } + } + } + } + } + + m_symfile_spec = file; + m_symfile_ap.reset(); + m_did_load_symbol_vendor = false; +} + +bool +Module::IsExecutable () +{ + if (GetObjectFile() == NULL) + return false; + else + return GetObjectFile()->IsExecutable(); +} + +bool +Module::IsLoadedInTarget (Target *target) +{ + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + { + SectionList *sections = GetSectionList(); + if (sections != NULL) + { + 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, Error& error, Stream* feedback_stream) +{ + if (!target) + { + error.SetErrorString("invalid destination Target"); + return false; + } + + LoadScriptFromSymFile shoud_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); + + 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); + + + const uint32_t num_specs = file_specs.GetSize(); + if (num_specs) + { + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + if (script_interpreter) + { + for (uint32_t i=0; iPrintf("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.IsExactMatch(new_arch); +} + +bool +Module::SetLoadAddress (Target &target, lldb::addr_t offset, bool &changed) +{ + size_t num_loaded_sections = 0; + SectionList *section_list = GetSectionList (); + if (section_list) + { + 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)); + // Only load non-thread specific sections when given a slide + if (section_sp && !section_sp->IsThreadSpecific()) + { + if (target.GetSectionLoadList().SetSectionLoadAddress (section_sp, section_sp->GetFileAddress() + offset)) + ++num_loaded_sections; + } + } + } + changed = num_loaded_sections > 0; + return num_loaded_sections > 0; +} + + +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... + if (uuid == GetUUID()) + return true; + else + return false; + } + + const FileSpec &file_spec = module_ref.GetFileSpec(); + if (file_spec) + { + if (!FileSpec::Equal (file_spec, m_file, file_spec.GetDirectory())) + return false; + } + + const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec(); + if (platform_file_spec) + { + if (!FileSpec::Equal (platform_file_spec, GetPlatformFileSpec (), platform_file_spec.GetDirectory())) + return false; + } + + const ArchSpec &arch = module_ref.GetArchitecture(); + if (arch.IsValid()) + { + if (!m_arch.IsCompatibleMatch(arch)) + return false; + } + + const 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 +{ + Mutex::Locker locker (m_mutex); + return m_source_mappings.FindFile (orig_spec, new_spec); +} + +bool +Module::RemapSourceFile (const char *path, std::string &new_path) const +{ + Mutex::Locker locker (m_mutex); + return m_source_mappings.RemapPath(path, new_path); +} + +uint32_t +Module::GetVersion (uint32_t *versions, uint32_t num_versions) +{ + ObjectFile *obj_file = GetObjectFile(); + if (obj_file) + return obj_file->GetVersion (versions, num_versions); + + if (versions && num_versions) + { + for (uint32_t i=0; iModuleAdded(*this, module_sp); + } +} + +void +ModuleList::Append (const ModuleSP &module_sp) +{ + AppendImpl (module_sp); +} + +void +ModuleList::ReplaceEquivalent (const ModuleSP &module_sp) +{ + if (module_sp) + { + Mutex::Locker locker(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) +{ + if (module_sp) + { + Mutex::Locker locker(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); + 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) + { + Mutex::Locker locker(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->ModuleRemoved(*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->ModuleRemoved(*this, module_sp); + return retval; +} + +bool +ModuleList::Remove (const ModuleSP &module_sp) +{ + return RemoveImpl (module_sp); +} + +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->ModuleUpdated(*this, old_module_sp,new_module_sp); + return true; +} + +bool +ModuleList::RemoveIfOrphaned (const Module *module_ptr) +{ + if (module_ptr) + { + Mutex::Locker locker(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) +{ + Mutex::Locker locker; + + if (mandatory) + { + locker.Lock (m_modules_mutex); + } + else + { + // Not mandatory, remove orphans if we can get the mutex + if (!locker.TryLock(m_modules_mutex)) + 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) +{ + Mutex::Locker locker(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)) + ++num_removed; + } + return num_removed; +} + + +void +ModuleList::Clear() +{ + ClearImpl(); +} + +void +ModuleList::Destroy() +{ + ClearImpl(); +} + +void +ModuleList::ClearImpl (bool use_notifier) +{ + Mutex::Locker locker(m_modules_mutex); + if (use_notifier && m_notifier) + m_notifier->WillClearList(*this); + m_modules.clear(); +} + +Module* +ModuleList::GetModulePointerAtIndex (size_t idx) const +{ + Mutex::Locker locker(m_modules_mutex); + return GetModulePointerAtIndexUnlocked(idx); +} + +Module* +ModuleList::GetModulePointerAtIndexUnlocked (size_t idx) const +{ + if (idx < m_modules.size()) + return m_modules[idx].get(); + return NULL; +} + +ModuleSP +ModuleList::GetModuleAtIndex(size_t idx) const +{ + Mutex::Locker locker(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 (const ConstString &name, + uint32_t 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) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctions (lookup_name, + NULL, + lookup_name_type_mask, + include_symbols, + include_inlines, + true, + sc_list); + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (iFindFunctions (name, NULL, name_type_mask, include_symbols, include_inlines, true, sc_list); + } + } + return sc_list.GetSize() - old_size; +} + +size_t +ModuleList::FindFunctionSymbols (const ConstString &name, + uint32_t name_type_mask, + SymbolContextList& sc_list) +{ + const size_t old_size = sc_list.GetSize(); + + if (name_type_mask & eFunctionNameTypeAuto) + { + ConstString lookup_name; + uint32_t lookup_name_type_mask = 0; + bool match_name_after_lookup = false; + Module::PrepareForFunctionNameLookup (name, name_type_mask, + lookup_name, + lookup_name_type_mask, + match_name_after_lookup); + + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindFunctionSymbols (lookup_name, + lookup_name_type_mask, + sc_list); + } + + if (match_name_after_lookup) + { + SymbolContext sc; + size_t i = old_size; + while (iFindFunctionSymbols (name, name_type_mask, 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(); + + Mutex::Locker locker(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 (const ConstString &name, + bool append, + size_t max_matches, + VariableList& variable_list) const +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (name, NULL, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +size_t +ModuleList::FindGlobalVariables (const RegularExpression& regex, + bool append, + size_t max_matches, + VariableList& variable_list) const +{ + size_t initial_size = variable_list.GetSize(); + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindGlobalVariables (regex, append, max_matches, variable_list); + } + return variable_list.GetSize() - initial_size; +} + + +size_t +ModuleList::FindSymbolsWithNameAndType (const ConstString &name, + SymbolType symbol_type, + SymbolContextList &sc_list, + bool append) const +{ + Mutex::Locker locker(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 ®ex, + lldb::SymbolType symbol_type, + SymbolContextList &sc_list, + bool append) const +{ + Mutex::Locker locker(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(); + + Mutex::Locker locker(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" + { + Mutex::Locker locker(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()) + { + Mutex::Locker locker(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 (const SymbolContext& sc, const ConstString &name, bool name_is_fully_qualified, size_t max_matches, TypeList& types) const +{ + Mutex::Locker locker(m_modules_mutex); + + size_t total_matches = 0; + collection::const_iterator pos, end = m_modules.end(); + if (sc.module_sp) + { + // The symbol context "sc" contains a module so we want to search that + // one first if it is in our list... + for (pos = m_modules.begin(); pos != end; ++pos) + { + if (sc.module_sp.get() == (*pos).get()) + { + total_matches += (*pos)->FindTypes (sc, name, name_is_fully_qualified, max_matches, types); + + if (total_matches >= max_matches) + break; + } + } + } + + if (total_matches < max_matches) + { + SymbolContext world_sc; + 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 comparisong will always be true (valid_module_ptr != NULL). + if (sc.module_sp.get() != (*pos).get()) + total_matches += (*pos)->FindTypes (world_sc, name, name_is_fully_qualified, max_matches, types); + + if (total_matches >= max_matches) + break; + } + } + + return total_matches; +} + +bool +ModuleList::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const +{ + Mutex::Locker locker(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; +} + + + +ModuleSP +ModuleList::FindFirstModule (const ModuleSpec &module_spec) const +{ + ModuleSP module_sp; + Mutex::Locker locker(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; + { + Mutex::Locker locker(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"; + + Mutex::Locker locker(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) + { + Mutex::Locker locker(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 +{ + Mutex::Locker locker(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, uint32_t 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 + { + Mutex::Locker locker(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, + uint32_t resolve_scope, + SymbolContextList& sc_list +) const +{ + FileSpec file_spec(file_path, false); + 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, uint32_t resolve_scope, SymbolContextList& sc_list) const +{ + Mutex::Locker locker(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) + { + Mutex::Locker locker(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; +} + +static ModuleList & +GetSharedModuleList () +{ + // 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. + static ModuleList *g_shared_module_list = NULL; + if (g_shared_module_list == NULL) + g_shared_module_list = new ModuleList(); // <--- Intentional leak!!! + + return *g_shared_module_list; +} + +bool +ModuleList::ModuleIsInCache (const Module *module_ptr) +{ + if (module_ptr) + { + ModuleList &shared_module_list = GetSharedModuleList (); + return shared_module_list.FindModule (module_ptr).get() != NULL; + } + 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); +} + +Error +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 (); + Mutex::Locker locker(shared_module_list.m_modules_mutex); + char path[PATH_MAX]; + + Error 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 == false) + { + 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->get()) + *old_module_sp_ptr = module_sp; + + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_MODULES)); + if (log) + log->Printf("module changed: %p, removing from global module list", 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; + else + { + module_sp.reset (new 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) + { + 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 (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + return error; + } + } + 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. + FileSpec file_spec = Symbols::LocateExecutableObjectFile (module_spec); + + // Don't look for the file if it appears to be the same one we already + // checked for above... + if (file_spec != module_file_spec) + { + if (!file_spec.Exists()) + { + file_spec.GetPath(path, sizeof(path)); + if (path[0] == '\0') + module_file_spec.GetPath(path, sizeof(path)); + if (file_spec.Exists()) + { + 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() = file_spec; + platform_module_spec.GetPlatformFileSpec() = file_spec; + 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() == NULL) + { + TimeValue file_spec_mod_time(file_spec.GetModificationTime()); + if (file_spec_mod_time.IsValid()) + { + 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.get() == NULL) + { + module_sp.reset (new 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 (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + } + else + { + file_spec.GetPath(path, sizeof(path)); + + if (file_spec) + { + 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& errors, + Stream *feedback_stream, + bool continue_on_error) +{ + if (!target) + return false; + Mutex::Locker locker(m_modules_mutex); + for (auto module : m_modules) + { + Error 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.size() == 0; +} diff --git a/source/Core/Opcode.cpp b/source/Core/Opcode.cpp new file mode 100644 index 00000000000..d9878656e3f --- /dev/null +++ b/source/Core/Opcode.cpp @@ -0,0 +1,134 @@ +//===-- Opcode.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Opcode.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ADT/Triple.h" +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" + + +using namespace lldb; +using namespace lldb_private; + + +int +Opcode::Dump (Stream *s, uint32_t min_byte_width) +{ + int bytes_written = 0; + switch (m_type) + { + case Opcode::eTypeInvalid: + bytes_written = s->PutCString (""); + break; + case Opcode::eType8: + bytes_written = s->Printf ("0x%2.2x", m_data.inst8); + break; + case Opcode::eType16: + bytes_written = s->Printf ("0x%4.4x", m_data.inst16); + break; + case Opcode::eType16_2: + case Opcode::eType32: + bytes_written = s->Printf ("0x%8.8x", m_data.inst32); + break; + + case Opcode::eType64: + bytes_written = s->Printf ("0x%16.16" PRIx64, m_data.inst64); + break; + + case Opcode::eTypeBytes: + { + for (uint32_t i=0; i 0) + bytes_written += s->PutChar (' '); + bytes_written += s->Printf ("%2.2x", m_data.inst.bytes[i]); + } + } + break; + } + + // Add spaces to make sure bytes dispay comes out even in case opcodes + // aren't all the same size + if (bytes_written < min_byte_width) + bytes_written = s->Printf ("%*s", min_byte_width - bytes_written, ""); + return bytes_written; +} + +lldb::ByteOrder +Opcode::GetDataByteOrder () const +{ + switch (m_type) + { + case Opcode::eTypeInvalid: break; + case Opcode::eType8: + case Opcode::eType16: + case Opcode::eType16_2: + case Opcode::eType32: + case Opcode::eType64: return lldb::endian::InlHostByteOrder(); + case Opcode::eTypeBytes: + break; + } + return eByteOrderInvalid; +} + +uint32_t +Opcode::GetData (DataExtractor &data) const +{ + uint32_t byte_size = GetByteSize (); + + DataBufferSP buffer_sp; + if (byte_size > 0) + { + switch (m_type) + { + case Opcode::eTypeInvalid: + break; + + case Opcode::eType8: buffer_sp.reset (new DataBufferHeap (&m_data.inst8, byte_size)); break; + case Opcode::eType16: buffer_sp.reset (new DataBufferHeap (&m_data.inst16, byte_size)); break; + case Opcode::eType16_2: + { + // 32 bit thumb instruction, we need to sizzle this a bit + uint8_t buf[4]; + buf[0] = m_data.inst.bytes[2]; + buf[1] = m_data.inst.bytes[3]; + buf[2] = m_data.inst.bytes[0]; + buf[3] = m_data.inst.bytes[1]; + buffer_sp.reset (new DataBufferHeap (buf, byte_size)); + } + break; + case Opcode::eType32: + buffer_sp.reset (new DataBufferHeap (&m_data.inst32, byte_size)); + break; + case Opcode::eType64: buffer_sp.reset (new DataBufferHeap (&m_data.inst64, byte_size)); break; + case Opcode::eTypeBytes:buffer_sp.reset (new DataBufferHeap (GetOpcodeBytes(), byte_size)); break; + break; + } + } + + if (buffer_sp) + { + data.SetByteOrder(GetDataByteOrder()); + data.SetData (buffer_sp); + return byte_size; + } + data.Clear(); + return 0; +} + + + diff --git a/source/Core/PluginManager.cpp b/source/Core/PluginManager.cpp new file mode 100644 index 00000000000..7a2d3772e6c --- /dev/null +++ b/source/Core/PluginManager.cpp @@ -0,0 +1,2064 @@ +//===-- PluginManager.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/PluginManager.h" + +#include + +#include +#include + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Host.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Interpreter/OptionValueProperties.h" + +#include "llvm/ADT/StringRef.h" + +using namespace lldb; +using namespace lldb_private; + +enum PluginAction +{ + ePluginRegisterInstance, + ePluginUnregisterInstance, + ePluginGetInstanceAtIndex +}; + + +typedef bool (*PluginInitCallback) (void); +typedef void (*PluginTermCallback) (void); + +struct PluginInfo +{ + void *plugin_handle; + PluginInitCallback plugin_init_callback; + PluginTermCallback plugin_term_callback; +}; + +typedef std::map PluginTerminateMap; + +static Mutex & +GetPluginMapMutex () +{ + static Mutex g_plugin_map_mutex (Mutex::eMutexTypeRecursive); + 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) +{ + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetPluginMapMutex ()); + PluginTerminateMap &plugin_map = GetPluginMap (); + assert (plugin_map.find (plugin_file_spec) == plugin_map.end()); + plugin_map[plugin_file_spec] = plugin_info; +} + + +static FileSpec::EnumerateDirectoryResult +LoadPluginCallback +( + void *baton, + FileSpec::FileType file_type, + const FileSpec &file_spec +) +{ +// PluginManager *plugin_manager = (PluginManager *)baton; + Error error; + + // 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 (file_type == FileSpec::eFileTypeRegular || + file_type == FileSpec::eFileTypeSymbolicLink || + file_type == FileSpec::eFileTypeUnknown ) + { + FileSpec plugin_file_spec (file_spec); + plugin_file_spec.ResolvePath(); + + if (PluginIsLoaded (plugin_file_spec)) + return FileSpec::eEnumerateDirectoryResultNext; + else + { + PluginInfo plugin_info = { NULL, NULL, NULL }; + uint32_t flags = Host::eDynamicLibraryOpenOptionLazy | + Host::eDynamicLibraryOpenOptionLocal | + Host::eDynamicLibraryOpenOptionLimitGetSymbol; + + plugin_info.plugin_handle = Host::DynamicLibraryOpen (plugin_file_spec, flags, error); + if (plugin_info.plugin_handle) + { + bool success = false; + plugin_info.plugin_init_callback = (PluginInitCallback)Host::DynamicLibraryGetSymbol (plugin_info.plugin_handle, "LLDBPluginInitialize", error); + 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 NULL + plugin_info.plugin_term_callback = (PluginTermCallback)Host::DynamicLibraryGetSymbol (plugin_info.plugin_handle, "LLDBPluginTerminate", error); + } + 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. + Host::DynamicLibraryClose (plugin_info.plugin_handle); + plugin_info.plugin_handle = NULL; + plugin_info.plugin_init_callback = NULL; + } + + // 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 FileSpec::eEnumerateDirectoryResultNext; + } + } + } + + if (file_type == FileSpec::eFileTypeUnknown || + file_type == FileSpec::eFileTypeDirectory || + file_type == FileSpec::eFileTypeSymbolicLink ) + { + // 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 enurating a file system that doesn't have correct file type + // information. + return FileSpec::eEnumerateDirectoryResultEnter; + } + + return FileSpec::eEnumerateDirectoryResultNext; +} + + +void +PluginManager::Initialize () +{ +#if 1 + FileSpec dir_spec; + const bool find_directories = true; + const bool find_files = true; + const bool find_other = true; + char dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypeLLDBSystemPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + NULL); + } + } + + if (Host::GetLLDBPath (ePathTypeLLDBUserPlugins, dir_spec)) + { + if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path))) + { + FileSpec::EnumerateDirectory (dir_path, + find_directories, + find_files, + find_other, + LoadPluginCallback, + NULL); + } + } +#endif +} + +void +PluginManager::Terminate () +{ + Mutex::Locker locker (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 NULL). + if (pos->second.plugin_handle) + { + if (pos->second.plugin_term_callback) + pos->second.plugin_term_callback(); + Host::DynamicLibraryClose (pos->second.plugin_handle); + } + } + plugin_map.clear(); +} + + +#pragma mark ABI + + +struct ABIInstance +{ + ABIInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + ABICreateInstance create_callback; +}; + +typedef std::vector ABIInstances; + +static Mutex & +GetABIInstancesMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ABIInstances & +GetABIInstances () +{ + static ABIInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetABIInstancesMutex ()); + GetABIInstances ().push_back (instance); + return true; + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ABICreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetABIInstancesMutex ()); + ABIInstances &instances = GetABIInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +ABICreateInstance +PluginManager::GetABICreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +#pragma mark Disassembler + + +struct DisassemblerInstance +{ + DisassemblerInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + DisassemblerCreateInstance create_callback; +}; + +typedef std::vector DisassemblerInstances; + +static Mutex & +GetDisassemblerMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static DisassemblerInstances & +GetDisassemblerInstances () +{ + static DisassemblerInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetDisassemblerMutex ()); + GetDisassemblerInstances ().push_back (instance); + return true; + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DisassemblerCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetDisassemblerMutex ()); + DisassemblerInstances &instances = GetDisassemblerInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +DisassemblerCreateInstance +PluginManager::GetDisassemblerCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + + +#pragma mark DynamicLoader + + +struct DynamicLoaderInstance +{ + DynamicLoaderInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback (NULL) + { + } + + ConstString name; + std::string description; + DynamicLoaderCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector DynamicLoaderInstances; + + +static Mutex & +GetDynamicLoaderMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static DynamicLoaderInstances & +GetDynamicLoaderInstances () +{ + static DynamicLoaderInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetDynamicLoaderMutex ()); + GetDynamicLoaderInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (DynamicLoaderCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetDynamicLoaderMutex ()); + DynamicLoaderInstances &instances = GetDynamicLoaderInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +DynamicLoaderCreateInstance +PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +#pragma mark EmulateInstruction + + +struct EmulateInstructionInstance +{ + EmulateInstructionInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + EmulateInstructionCreateInstance create_callback; +}; + +typedef std::vector EmulateInstructionInstances; + +static Mutex & +GetEmulateInstructionMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static EmulateInstructionInstances & +GetEmulateInstructionInstances () +{ + static EmulateInstructionInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetEmulateInstructionMutex ()); + GetEmulateInstructionInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (EmulateInstructionCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetEmulateInstructionMutex ()); + EmulateInstructionInstances &instances = GetEmulateInstructionInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +EmulateInstructionCreateInstance +PluginManager::GetEmulateInstructionCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} +#pragma mark OperatingSystem + + +struct OperatingSystemInstance +{ + OperatingSystemInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + OperatingSystemCreateInstance create_callback; +}; + +typedef std::vector OperatingSystemInstances; + +static Mutex & +GetOperatingSystemMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static OperatingSystemInstances & +GetOperatingSystemInstances () +{ + static OperatingSystemInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + OperatingSystemCreateInstance create_callback) +{ + if (create_callback) + { + OperatingSystemInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetOperatingSystemMutex ()); + GetOperatingSystemInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (OperatingSystemCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetOperatingSystemMutex ()); + OperatingSystemInstances &instances = GetOperatingSystemInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +OperatingSystemCreateInstance +PluginManager::GetOperatingSystemCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +#pragma mark LanguageRuntime + + +struct LanguageRuntimeInstance +{ + LanguageRuntimeInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + LanguageRuntimeCreateInstance create_callback; +}; + +typedef std::vector LanguageRuntimeInstances; + +static Mutex & +GetLanguageRuntimeMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static LanguageRuntimeInstances & +GetLanguageRuntimeInstances () +{ + static LanguageRuntimeInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + LanguageRuntimeCreateInstance create_callback +) +{ + if (create_callback) + { + LanguageRuntimeInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + GetLanguageRuntimeInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LanguageRuntimeCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +LanguageRuntimeCreateInstance +PluginManager::GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +#pragma mark ObjectFile + +struct ObjectFileInstance +{ + ObjectFileInstance() : + name(), + description(), + create_callback(NULL), + create_memory_callback (NULL), + get_module_specifications (NULL) + { + } + + ConstString name; + std::string description; + ObjectFileCreateInstance create_callback; + ObjectFileCreateMemoryInstance create_memory_callback; + ObjectFileGetModuleSpecifications get_module_specifications; +}; + +typedef std::vector ObjectFileInstances; + +static Mutex & +GetObjectFileMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ObjectFileInstances & +GetObjectFileInstances () +{ + static ObjectFileInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + ObjectFileCreateInstance create_callback, + ObjectFileCreateMemoryInstance create_memory_callback, + ObjectFileGetModuleSpecifications get_module_specifications) +{ + 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.get_module_specifications = get_module_specifications; + Mutex::Locker locker (GetObjectFileMutex ()); + GetObjectFileInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectFileCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +ObjectFileCreateMemoryInstance +PluginManager::GetObjectFileCreateMemoryCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].create_memory_callback; + return NULL; +} + +ObjectFileGetModuleSpecifications +PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectFileMutex ()); + ObjectFileInstances &instances = GetObjectFileInstances (); + if (idx < instances.size()) + return instances[idx].get_module_specifications; + return NULL; +} + +ObjectFileCreateInstance +PluginManager::GetObjectFileCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +ObjectFileCreateMemoryInstance +PluginManager::GetObjectFileCreateMemoryCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + + +#pragma mark ObjectContainer + +struct ObjectContainerInstance +{ + ObjectContainerInstance() : + name(), + description(), + create_callback (NULL), + get_module_specifications (NULL) + { + } + + ConstString name; + std::string description; + ObjectContainerCreateInstance create_callback; + ObjectFileGetModuleSpecifications get_module_specifications; + +}; + +typedef std::vector ObjectContainerInstances; + +static Mutex & +GetObjectContainerMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ObjectContainerInstances & +GetObjectContainerInstances () +{ + static ObjectContainerInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin (const 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; + Mutex::Locker locker (GetObjectContainerMutex ()); + GetObjectContainerInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (ObjectContainerCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +ObjectContainerCreateInstance +PluginManager::GetObjectContainerCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +ObjectFileGetModuleSpecifications +PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetObjectContainerMutex ()); + ObjectContainerInstances &instances = GetObjectContainerInstances (); + if (idx < instances.size()) + return instances[idx].get_module_specifications; + return NULL; +} + +#pragma mark LogChannel + +struct LogInstance +{ + LogInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + LogChannelCreateInstance create_callback; +}; + +typedef std::vector LogInstances; + +static Mutex & +GetLogMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static LogInstances & +GetLogInstances () +{ + static LogInstances g_instances; + return g_instances; +} + + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + LogChannelCreateInstance create_callback +) +{ + if (create_callback) + { + LogInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetLogMutex ()); + GetLogInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (LogChannelCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + + LogInstances::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; +} + +const char * +PluginManager::GetLogChannelCreateNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +LogChannelCreateInstance +PluginManager::GetLogChannelCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetLogMutex ()); + LogInstances &instances = GetLogInstances (); + + LogInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + +#pragma mark Platform + +struct PlatformInstance +{ + PlatformInstance() : + name(), + description(), + create_callback(NULL), + debugger_init_callback (NULL) + { + } + + ConstString name; + std::string description; + PlatformCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector PlatformInstances; + +static Mutex & +GetPlatformInstancesMutex () +{ + static Mutex g_platform_instances_mutex (Mutex::eMutexTypeRecursive); + return g_platform_instances_mutex; +} + +static PlatformInstances & +GetPlatformInstances () +{ + static PlatformInstances g_platform_instances; + return g_platform_instances; +} + + +bool +PluginManager::RegisterPlugin (const ConstString &name, + const char *description, + PlatformCreateInstance create_callback, + DebuggerInitializeCallback debugger_init_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + +const char * +PluginManager::GetPlatformPluginDescriptionAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].description.c_str(); + return NULL; +} + +bool +PluginManager::UnregisterPlugin (PlatformCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetPlatformInstancesMutex ()); + PlatformInstances &instances = GetPlatformInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +PlatformCreateInstance +PluginManager::GetPlatformCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +size_t +PluginManager::AutoCompletePlatformName (const char *name, StringList &matches) +{ + if (name) + { + Mutex::Locker locker (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(NULL), + debugger_init_callback(NULL) + { + } + + ConstString name; + std::string description; + ProcessCreateInstance create_callback; + DebuggerInitializeCallback debugger_init_callback; +}; + +typedef std::vector ProcessInstances; + +static Mutex & +GetProcessMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static ProcessInstances & +GetProcessInstances () +{ + static ProcessInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin (const 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; + Mutex::Locker locker (GetProcessMutex ()); + GetProcessInstances ().push_back (instance); + } + return false; +} + +const char * +PluginManager::GetProcessPluginNameAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].name.GetCString(); + return NULL; +} + +const char * +PluginManager::GetProcessPluginDescriptionAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].description.c_str(); + return NULL; +} + +bool +PluginManager::UnregisterPlugin (ProcessCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetProcessMutex ()); + ProcessInstances &instances = GetProcessInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +ProcessCreateInstance +PluginManager::GetProcessCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +#pragma mark SymbolFile + +struct SymbolFileInstance +{ + SymbolFileInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SymbolFileCreateInstance create_callback; +}; + +typedef std::vector SymbolFileInstances; + +static Mutex & +GetSymbolFileMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SymbolFileInstances & +GetSymbolFileInstances () +{ + static SymbolFileInstances g_instances; + return g_instances; +} + + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + SymbolFileCreateInstance create_callback +) +{ + if (create_callback) + { + SymbolFileInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetSymbolFileMutex ()); + GetSymbolFileInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolFileCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetSymbolFileMutex ()); + SymbolFileInstances &instances = GetSymbolFileInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +SymbolFileCreateInstance +PluginManager::GetSymbolFileCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + + +#pragma mark SymbolVendor + +struct SymbolVendorInstance +{ + SymbolVendorInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SymbolVendorCreateInstance create_callback; +}; + +typedef std::vector SymbolVendorInstances; + +static Mutex & +GetSymbolVendorMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SymbolVendorInstances & +GetSymbolVendorInstances () +{ + static SymbolVendorInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetSymbolVendorMutex ()); + GetSymbolVendorInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SymbolVendorCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetSymbolVendorMutex ()); + SymbolVendorInstances &instances = GetSymbolVendorInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +SymbolVendorCreateInstance +PluginManager::GetSymbolVendorCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + + +#pragma mark UnwindAssembly + +struct UnwindAssemblyInstance +{ + UnwindAssemblyInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + UnwindAssemblyCreateInstance create_callback; +}; + +typedef std::vector UnwindAssemblyInstances; + +static Mutex & +GetUnwindAssemblyMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static UnwindAssemblyInstances & +GetUnwindAssemblyInstances () +{ + static UnwindAssemblyInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const 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; + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + GetUnwindAssemblyInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (UnwindAssemblyCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (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) +{ + Mutex::Locker locker (GetUnwindAssemblyMutex ()); + UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + + +UnwindAssemblyCreateInstance +PluginManager::GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (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 NULL; +} + +void +PluginManager::DebuggerInitialize (Debugger &debugger) +{ + // Initialize the DynamicLoader plugins + { + Mutex::Locker locker (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 Platform plugins + { + Mutex::Locker locker (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 + { + Mutex::Locker locker (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); + } + } + +} + +// This is the preferred new way to register plugin specific settings. e.g. +// This will put a plugin's settings under e.g. "plugin...SETTINGNAME". +static lldb::OptionValuePropertiesSP +GetDebuggerPropertyForPlugins (Debugger &debugger, + const ConstString &plugin_type_name, + const 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 (NULL, g_property_name); + if (!plugin_properties_sp && can_create) + { + plugin_properties_sp.reset (new 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 (NULL, plugin_type_name); + if (!plugin_type_properties_sp && can_create) + { + plugin_type_properties_sp.reset (new 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..SETTINGNAME" +// and Platform generic settings would be under "platform.SETTINGNAME". +static lldb::OptionValuePropertiesSP +GetDebuggerPropertyForPluginsOldStyle (Debugger &debugger, + const ConstString &plugin_type_name, + const 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 (NULL, plugin_type_name); + if (!plugin_properties_sp && can_create) + { + plugin_properties_sp.reset (new 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 (NULL, g_property_name); + if (!plugin_type_properties_sp && can_create) + { + plugin_type_properties_sp.reset (new 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(); +} + + +lldb::OptionValuePropertiesSP +PluginManager::GetSettingForDynamicLoaderPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("dynamic-loader"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForDynamicLoaderPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("dynamic-loader"), + ConstString("Settings for dynamic loader 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::GetSettingForPlatformPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPluginsOldStyle (debugger, + ConstString("platform"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForPlatformPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPluginsOldStyle (debugger, + ConstString("platform"), + ConstString("Settings for platform 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::GetSettingForProcessPlugin (Debugger &debugger, const ConstString &setting_name) +{ + lldb::OptionValuePropertiesSP properties_sp; + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("process"), + ConstString(), // not creating to so we don't need the description + false)); + if (plugin_type_properties_sp) + properties_sp = plugin_type_properties_sp->GetSubProperty (NULL, setting_name); + return properties_sp; +} + +bool +PluginManager::CreateSettingForProcessPlugin (Debugger &debugger, + const lldb::OptionValuePropertiesSP &properties_sp, + const ConstString &description, + bool is_global_property) +{ + if (properties_sp) + { + lldb::OptionValuePropertiesSP plugin_type_properties_sp (GetDebuggerPropertyForPlugins (debugger, + ConstString("process"), + ConstString("Settings for process 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; +} + diff --git a/source/Core/RegisterValue.cpp b/source/Core/RegisterValue.cpp new file mode 100644 index 00000000000..91f5bea805c --- /dev/null +++ b/source/Core/RegisterValue.cpp @@ -0,0 +1,1272 @@ +//===-- RegisterValue.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RegisterValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + + +bool +RegisterValue::Dump (Stream *s, + const RegisterInfo *reg_info, + bool prefix_with_name, + bool prefix_with_alt_name, + Format format, + uint32_t reg_name_right_align_at) const +{ + DataExtractor data; + if (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"); + const char *fmt = format_string.GetData(); + if (prefix_with_name) + { + if (reg_info->name) + { + s->Printf (fmt, reg_info->name); + name_printed = true; + } + else if (reg_info->alt_name) + { + s->Printf (fmt, 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, 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, reg_info->name); + name_printed = true; + } + } + if (name_printed) + s->PutCString (" = "); + + if (format == eFormatDefault) + format = reg_info->format; + + data.Dump (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; +} + + +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, + Error &error) const +{ + if (reg_info == NULL) + { + error.SetErrorString ("invalid register info argument."); + return 0; + } + + // ReadRegister should have already been called on tgus 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, + Error &error) +{ + if (reg_info == NULL) + { + 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 + // + // Error! (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); + + // Given the register info, set the value type of this RegisterValue object + SetType (reg_info); + // And make sure we were able to figure out what that register value was + RegisterValue::Type value_type = GetType(); + if (value_type == eTypeInvalid) + { + // No value has been read into this object... + error.SetErrorStringWithFormat("invalid register value type for register %s", reg_info->name); + return 0; + } + else if (value_type == eTypeBytes) + { + m_data.buffer.byte_order = src_byte_order; + // Make sure to set the buffer length of the destination buffer to avoid + // problems due to uninitalized variables. + m_data.buffer.length = src_len; + } + + const uint32_t bytes_copied = src_data.CopyByteOrderedData (0, // src offset + src_len, // src length + GetBytes(), // dst buffer + GetByteSize(), // dst length + GetByteOrder()); // dst byte order + if (bytes_copied == 0) + error.SetErrorStringWithFormat("failed to copy data for register write of %s", reg_info->name); + + return bytes_copied; +} + +bool +RegisterValue::GetScalarValue (Scalar &scalar) const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: scalar = m_data.uint8; return true; + case 2: scalar = m_data.uint16; return true; + case 4: scalar = m_data.uint32; return true; + case 8: scalar = m_data.uint64; return true; + } + } + case eTypeUInt8: scalar = m_data.uint8; return true; + case eTypeUInt16: scalar = m_data.uint16; return true; + case eTypeUInt32: scalar = m_data.uint32; return true; + case eTypeUInt64: scalar = m_data.uint64; return true; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: break; +#endif + case eTypeFloat: scalar = m_data.ieee_float; return true; + case eTypeDouble: scalar = m_data.ieee_double; return true; + case eTypeLongDouble: scalar = m_data.ieee_long_double; return true; + } + return false; +} + +void +RegisterValue::Clear() +{ + m_type = eTypeInvalid; +} + +RegisterValue::Type +RegisterValue::SetType (const RegisterInfo *reg_info) +{ + m_type = eTypeInvalid; + const uint32_t byte_size = reg_info->byte_size; + switch (reg_info->encoding) + { + case eEncodingInvalid: + break; + + case eEncodingUint: + case eEncodingSint: + if (byte_size == 1) + m_type = eTypeUInt8; + else if (byte_size <= 2) + m_type = eTypeUInt16; + else if (byte_size <= 4) + m_type = eTypeUInt32; + else if (byte_size <= 8) + m_type = eTypeUInt64; +#if defined (ENABLE_128_BIT_SUPPORT) + else if (byte_size <= 16) + m_type = eTypeUInt128; +#endif + break; + + case eEncodingIEEE754: + if (byte_size == sizeof(float)) + m_type = eTypeFloat; + else if (byte_size == sizeof(double)) + m_type = eTypeDouble; + else if (byte_size == sizeof(long double)) + m_type = eTypeLongDouble; + break; + + case eEncodingVector: + m_type = eTypeBytes; + break; + } + return m_type; +} + +Error +RegisterValue::SetValueFromData (const RegisterInfo *reg_info, DataExtractor &src, lldb::offset_t src_offset, bool partial_data_ok) +{ + Error 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 (m_data.buffer.bytes, 0, sizeof (m_data.buffer.bytes)); + + switch (SetType (reg_info)) + { + case eTypeInvalid: + error.SetErrorString(""); + break; + case eTypeUInt8: SetUInt8 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt16: SetUInt16 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt32: SetUInt32 (src.GetMaxU32 (&src_offset, src_len)); break; + case eTypeUInt64: SetUInt64 (src.GetMaxU64 (&src_offset, src_len)); break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + { + __uint128_t data1 = src.GetU64 (&src_offset); + __uint128_t data2 = src.GetU64 (&src_offset); + if (src.GetByteSize() == eByteOrderBig) + SetUInt128 (data1 << 64 + data2); + else + SetUInt128 (data2 << 64 + data1); + } + break; +#endif + case eTypeFloat: SetFloat (src.GetFloat (&src_offset)); break; + case eTypeDouble: SetDouble(src.GetDouble (&src_offset)); break; + case eTypeLongDouble: SetFloat (src.GetLongDouble (&src_offset)); break; + case eTypeBytes: + { + m_data.buffer.length = reg_info->byte_size; + m_data.buffer.byte_order = src.GetByteOrder(); + assert (m_data.buffer.length <= kMaxRegisterByteSize); + if (m_data.buffer.length > kMaxRegisterByteSize) + m_data.buffer.length = kMaxRegisterByteSize; + if (src.CopyByteOrderedData (src_offset, // offset within "src" to start extracting data + src_len, // src length + m_data.buffer.bytes, // dst buffer + m_data.buffer.length, // dst length + m_data.buffer.byte_order) == 0)// dst byte order + { + error.SetErrorString ("data copy failed data."); + return error; + } + } + } + + return error; +} + +#include "llvm/ADT/StringRef.h" +#include +static inline void StripSpaces(llvm::StringRef &Str) +{ + while (!Str.empty() && isspace(Str[0])) + Str = Str.substr(1); + while (!Str.empty() && isspace(Str.back())) + Str = Str.substr(0, Str.size()-1); +} +static inline void LStrip(llvm::StringRef &Str, char c) +{ + if (!Str.empty() && Str.front() == c) + Str = Str.substr(1); +} +static inline void RStrip(llvm::StringRef &Str, char c) +{ + if (!Str.empty() && Str.back() == c) + Str = Str.substr(0, Str.size()-1); +} +// Helper function for RegisterValue::SetValueFromCString() +static bool +ParseVectorEncoding(const RegisterInfo *reg_info, const char *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}". + llvm::StringRef Str(vector_str); + StripSpaces(Str); + LStrip(Str, '{'); + RStrip(Str, '}'); + StripSpaces(Str); + + char Sep = ' '; + + // The first split should give us: + // ('0x2c', '0x4b 0x2a 0x3e 0xd0 0x4f 0x2a 0x3e 0xac 0x4a 0x2a 0x3e 0x84 0x4f 0x2a 0x3e'). + std::pair Pair = Str.split(Sep); + std::vector 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 (!Pair.first.getAsInteger(0, byte) && bytes.size() < byte_size) { + bytes.push_back(byte); + Pair = Pair.second.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; +} +Error +RegisterValue::SetValueFromCString (const RegisterInfo *reg_info, const char *value_str) +{ + Error error; + if (reg_info == NULL) + { + error.SetErrorString ("Invalid register info argument."); + return error; + } + + if (value_str == NULL || value_str[0] == '\0') + { + error.SetErrorString ("Invalid c-string value string."); + return error; + } + bool success = false; + const uint32_t byte_size = reg_info->byte_size; + switch (reg_info->encoding) + { + case eEncodingInvalid: + error.SetErrorString ("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size <= sizeof (uint64_t)) + { + uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid unsigned integer string value", value_str); + else 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); + else + { + if (!SetUInt (uval64, reg_info->byte_size)) + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %u", byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %u", byte_size); + return error; + } + break; + + case eEncodingSint: + if (byte_size <= sizeof (long long)) + { + uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat ("'%s' is not a valid signed integer string value", value_str); + else if (!Args::SInt64ValueIsValidForByteSize (sval64, byte_size)) + error.SetErrorStringWithFormat ("value 0x%" PRIx64 " is too large to fit in a %u byte signed integer value", sval64, byte_size); + else + { + if (!SetUInt (sval64, reg_info->byte_size)) + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %u", byte_size); + } + } + else + { + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %u", byte_size); + return error; + } + break; + + case eEncodingIEEE754: + if (byte_size == sizeof (float)) + { + if (::sscanf (value_str, "%f", &m_data.ieee_float) == 1) + m_type = eTypeFloat; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + else if (byte_size == sizeof (double)) + { + if (::sscanf (value_str, "%lf", &m_data.ieee_double) == 1) + m_type = eTypeDouble; + 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", &m_data.ieee_long_double) == 1) + m_type = eTypeLongDouble; + else + error.SetErrorStringWithFormat ("'%s' is not a valid float string value", value_str); + } + 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; + } + if (error.Fail()) + m_type = eTypeInvalid; + + return error; +} + + +bool +RegisterValue::SignExtend (uint32_t sign_bitpos) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (sign_bitpos == (8-1)) + return true; + else if (sign_bitpos < (8-1)) + { + uint8_t sign_bit = 1u << sign_bitpos; + if (m_data.uint8 & sign_bit) + { + const uint8_t mask = ~(sign_bit) + 1u; + m_data.uint8 |= mask; + } + return true; + } + break; + + case eTypeUInt16: + if (sign_bitpos == (16-1)) + return true; + else if (sign_bitpos < (16-1)) + { + uint16_t sign_bit = 1u << sign_bitpos; + if (m_data.uint16 & sign_bit) + { + const uint16_t mask = ~(sign_bit) + 1u; + m_data.uint16 |= mask; + } + return true; + } + break; + + case eTypeUInt32: + if (sign_bitpos == (32-1)) + return true; + else if (sign_bitpos < (32-1)) + { + uint32_t sign_bit = 1u << sign_bitpos; + if (m_data.uint32 & sign_bit) + { + const uint32_t mask = ~(sign_bit) + 1u; + m_data.uint32 |= mask; + } + return true; + } + break; + + case eTypeUInt64: + if (sign_bitpos == (64-1)) + return true; + else if (sign_bitpos < (64-1)) + { + uint64_t sign_bit = 1ull << sign_bitpos; + if (m_data.uint64 & sign_bit) + { + const uint64_t mask = ~(sign_bit) + 1ull; + m_data.uint64 |= mask; + } + return true; + } + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sign_bitpos == (128-1)) + return true; + else if (sign_bitpos < (128-1)) + { + __uint128_t sign_bit = (__uint128_t)1u << sign_bitpos; + if (m_data.uint128 & sign_bit) + { + const uint128_t mask = ~(sign_bit) + 1u; + m_data.uint128 |= mask; + } + return true; + } + break; +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + case eTypeBytes: + break; + } + return false; +} + +bool +RegisterValue::CopyValue (const RegisterValue &rhs) +{ + m_type = rhs.m_type; + switch (m_type) + { + case eTypeInvalid: + return false; + case eTypeUInt8: m_data.uint8 = rhs.m_data.uint8; break; + case eTypeUInt16: m_data.uint16 = rhs.m_data.uint16; break; + case eTypeUInt32: m_data.uint32 = rhs.m_data.uint32; break; + case eTypeUInt64: m_data.uint64 = rhs.m_data.uint64; break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: m_data.uint128 = rhs.m_data.uint128; break; +#endif + case eTypeFloat: m_data.ieee_float = rhs.m_data.ieee_float; break; + case eTypeDouble: m_data.ieee_double = rhs.m_data.ieee_double; break; + case eTypeLongDouble: m_data.ieee_long_double = rhs.m_data.ieee_long_double; break; + case eTypeBytes: + assert (rhs.m_data.buffer.length <= kMaxRegisterByteSize); + ::memcpy (m_data.buffer.bytes, rhs.m_data.buffer.bytes, kMaxRegisterByteSize); + m_data.buffer.length = rhs.m_data.buffer.length; + m_data.buffer.byte_order = rhs.m_data.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: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + } + } + 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: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeFloat: + if (sizeof(float) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(uint32_t)) + return m_data.uint32; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + } + } + 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: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeUInt64: return m_data.uint64; + case eTypeFloat: + if (sizeof(float) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(uint64_t)) + return m_data.uint64; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + case 8: return m_data.uint64; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +#if defined (ENABLE_128_BIT_SUPPORT) +__uint128_t +RegisterValue::GetAsUInt128 (__uint128_t fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt8: return m_data.uint8; + case eTypeUInt16: return m_data.uint16; + case eTypeUInt32: return m_data.uint32; + case eTypeUInt64: return m_data.uint64; + case eTypeUInt128: return m_data.uint128; + case eTypeFloat: + if (sizeof(float) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeDouble: + if (sizeof(double) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeLongDouble: + if (sizeof(long double) == sizeof(__uint128_t)) + return m_data.uint128; + break; + case eTypeBytes: + { + switch (m_data.buffer.length) + { + default: + break; + case 1: return m_data.uint8; + case 2: return m_data.uint16; + case 4: return m_data.uint32; + case 8: return m_data.uint64; + case 16: return m_data.uint128; + } + } + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} +#endif +float +RegisterValue::GetAsFloat (float fail_value, bool *success_ptr) const +{ + if (success_ptr) + *success_ptr = true; + switch (m_type) + { + default: break; + case eTypeUInt32: + if (sizeof(float) == sizeof(m_data.uint32)) + return m_data.ieee_float; + break; + case eTypeUInt64: + if (sizeof(float) == sizeof(m_data.uint64)) + return m_data.ieee_float; + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(float) == sizeof(m_data.uint128)) + return m_data.ieee_float; + break; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: + if (sizeof(float) == sizeof(double)) + return m_data.ieee_float; + break; + case eTypeLongDouble: + if (sizeof(float) == sizeof(long double)) + return m_data.ieee_float; + break; + } + 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: + if (sizeof(double) == sizeof(m_data.uint32)) + return m_data.ieee_double; + break; + + case eTypeUInt64: + if (sizeof(double) == sizeof(m_data.uint64)) + return m_data.ieee_double; + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(double) == sizeof(m_data.uint128)) + return m_data.ieee_double; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double; + + case eTypeLongDouble: + if (sizeof(double) == sizeof(long double)) + return m_data.ieee_double; + break; + } + 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: + if (sizeof(long double) == sizeof(m_data.uint32)) + return m_data.ieee_long_double; + break; + + case eTypeUInt64: + if (sizeof(long double) == sizeof(m_data.uint64)) + return m_data.ieee_long_double; + break; + +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (sizeof(long double) == sizeof(m_data.uint128)) + return m_data.ieee_long_double; +#endif + case eTypeFloat: return m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double; + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +const void * +RegisterValue::GetBytes () const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return &m_data.uint8; + case eTypeUInt16: return &m_data.uint16; + case eTypeUInt32: return &m_data.uint32; + case eTypeUInt64: return &m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return &m_data.uint128; +#endif + case eTypeFloat: return &m_data.ieee_float; + case eTypeDouble: return &m_data.ieee_double; + case eTypeLongDouble: return &m_data.ieee_long_double; + case eTypeBytes: return m_data.buffer.bytes; + } + return NULL; +} + +void * +RegisterValue::GetBytes () +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return &m_data.uint8; + case eTypeUInt16: return &m_data.uint16; + case eTypeUInt32: return &m_data.uint32; + case eTypeUInt64: return &m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return &m_data.uint128; +#endif + case eTypeFloat: return &m_data.ieee_float; + case eTypeDouble: return &m_data.ieee_double; + case eTypeLongDouble: return &m_data.ieee_long_double; + case eTypeBytes: return m_data.buffer.bytes; + } + return NULL; +} + +uint32_t +RegisterValue::GetByteSize () const +{ + switch (m_type) + { + case eTypeInvalid: break; + case eTypeUInt8: return sizeof(m_data.uint8); + case eTypeUInt16: return sizeof(m_data.uint16); + case eTypeUInt32: return sizeof(m_data.uint32); + case eTypeUInt64: return sizeof(m_data.uint64); +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return sizeof(m_data.uint128); +#endif + case eTypeFloat: return sizeof(m_data.ieee_float); + case eTypeDouble: return sizeof(m_data.ieee_double); + case eTypeLongDouble: return sizeof(m_data.ieee_long_double); + case eTypeBytes: return m_data.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); + } +#if defined (ENABLE_128_BIT_SUPPORT) + else if (byte_size <= 16) + { + SetUInt128 (uint); + } +#endif + 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 + // m_data.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 (m_data.buffer.bytes) && "Storing too many bytes in a RegisterValue."); + m_type = eTypeBytes; + m_data.buffer.length = length; + memcpy (m_data.buffer.bytes, bytes, length); + m_data.buffer.byte_order = byte_order; + } + else + { + m_type = eTypeInvalid; + m_data.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: return m_data.uint8 == rhs.m_data.uint8; + case eTypeUInt16: return m_data.uint16 == rhs.m_data.uint16; + case eTypeUInt32: return m_data.uint32 == rhs.m_data.uint32; + case eTypeUInt64: return m_data.uint64 == rhs.m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return m_data.uint128 == rhs.m_data.uint128; +#endif + case eTypeFloat: return m_data.ieee_float == rhs.m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double == rhs.m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double == rhs.m_data.ieee_long_double; + case eTypeBytes: + if (m_data.buffer.length != rhs.m_data.buffer.length) + return false; + else + { + uint8_t length = m_data.buffer.length; + if (length > kMaxRegisterByteSize) + length = kMaxRegisterByteSize; + return memcmp (m_data.buffer.bytes, rhs.m_data.buffer.bytes, length) == 0; + } + break; + } + } + return false; +} + +bool +RegisterValue::operator != (const RegisterValue &rhs) const +{ + if (m_type != rhs.m_type) + return true; + switch (m_type) + { + case eTypeInvalid: return false; + case eTypeUInt8: return m_data.uint8 != rhs.m_data.uint8; + case eTypeUInt16: return m_data.uint16 != rhs.m_data.uint16; + case eTypeUInt32: return m_data.uint32 != rhs.m_data.uint32; + case eTypeUInt64: return m_data.uint64 != rhs.m_data.uint64; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: return m_data.uint128 != rhs.m_data.uint128; +#endif + case eTypeFloat: return m_data.ieee_float != rhs.m_data.ieee_float; + case eTypeDouble: return m_data.ieee_double != rhs.m_data.ieee_double; + case eTypeLongDouble: return m_data.ieee_long_double != rhs.m_data.ieee_long_double; + case eTypeBytes: + if (m_data.buffer.length != rhs.m_data.buffer.length) + { + return true; + } + else + { + uint8_t length = m_data.buffer.length; + if (length > kMaxRegisterByteSize) + length = kMaxRegisterByteSize; + return memcmp (m_data.buffer.bytes, rhs.m_data.buffer.bytes, length) != 0; + } + break; + } + return true; +} + +bool +RegisterValue::ClearBit (uint32_t bit) +{ + switch (m_type) + { + case eTypeInvalid: + break; + + case eTypeUInt8: + if (bit < 8) + { + m_data.uint8 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt16: + if (bit < 16) + { + m_data.uint16 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt32: + if (bit < 32) + { + m_data.uint32 &= ~(1u << bit); + return true; + } + break; + + case eTypeUInt64: + if (bit < 64) + { + m_data.uint64 &= ~(1ull << (uint64_t)bit); + return true; + } + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (bit < 64) + { + m_data.uint128 &= ~((__uint128_t)1ull << (__uint128_t)bit); + return true; + } +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (m_data.buffer.byte_order == eByteOrderBig || m_data.buffer.byte_order == eByteOrderLittle) + { + uint32_t byte_idx; + if (m_data.buffer.byte_order == eByteOrderBig) + byte_idx = m_data.buffer.length - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < m_data.buffer.length) + { + m_data.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: + if (bit < 8) + { + m_data.uint8 |= (1u << bit); + return true; + } + break; + + case eTypeUInt16: + if (bit < 16) + { + m_data.uint16 |= (1u << bit); + return true; + } + break; + + case eTypeUInt32: + if (bit < 32) + { + m_data.uint32 |= (1u << bit); + return true; + } + break; + + case eTypeUInt64: + if (bit < 64) + { + m_data.uint64 |= (1ull << (uint64_t)bit); + return true; + } + break; +#if defined (ENABLE_128_BIT_SUPPORT) + case eTypeUInt128: + if (bit < 64) + { + m_data.uint128 |= ((__uint128_t)1ull << (__uint128_t)bit); + return true; + } +#endif + case eTypeFloat: + case eTypeDouble: + case eTypeLongDouble: + break; + + case eTypeBytes: + if (m_data.buffer.byte_order == eByteOrderBig || m_data.buffer.byte_order == eByteOrderLittle) + { + uint32_t byte_idx; + if (m_data.buffer.byte_order == eByteOrderBig) + byte_idx = m_data.buffer.length - (bit / 8) - 1; + else + byte_idx = bit / 8; + + const uint32_t byte_bit = bit % 8; + if (byte_idx < m_data.buffer.length) + { + m_data.buffer.bytes[byte_idx] |= (1u << byte_bit); + return true; + } + } + break; + } + return false; +} + diff --git a/source/Core/RegularExpression.cpp b/source/Core/RegularExpression.cpp new file mode 100644 index 00000000000..4ccd7748b13 --- /dev/null +++ b/source/Core/RegularExpression.cpp @@ -0,0 +1,279 @@ +//===-- RegularExpression.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/RegularExpression.h" +#include "llvm/ADT/StringRef.h" +#include + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +//---------------------------------------------------------------------- +RegularExpression::RegularExpression() : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(REG_EXTENDED) +{ + 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(const char* re, int flags) : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(flags) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re); +} + +//---------------------------------------------------------------------- +// Constructor that compiles "re" using "flags" and stores the +// resulting compiled regular expression into this object. +//---------------------------------------------------------------------- +RegularExpression::RegularExpression(const char* re) : + m_re(), + m_comp_err (1), + m_preg(), + m_compile_flags(REG_EXTENDED) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(re); +} + +RegularExpression::RegularExpression(const RegularExpression &rhs) +{ + memset(&m_preg,0,sizeof(m_preg)); + Compile(rhs.GetText(), rhs.GetCompileFlags()); +} + +const RegularExpression & +RegularExpression::operator= (const RegularExpression &rhs) +{ + if (&rhs != this) + { + Compile (rhs.GetText(), rhs.GetCompileFlags()); + } + return *this; +} +//---------------------------------------------------------------------- +// Destructor +// +// Any previosuly 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 compied 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 previosuly compiled regular +// expression contained in this object will be freed. +// +// RETURNS +// True of the refular expression compiles successfully, false +// otherwise. +//---------------------------------------------------------------------- +bool +RegularExpression::Compile(const char* re) +{ + return Compile (re, m_compile_flags); +} + +bool +RegularExpression::Compile(const char* re, int flags) +{ + Free(); + m_compile_flags = flags; + + if (re && re[0]) + { + m_re = re; + m_comp_err = ::regcomp (&m_preg, re, flags); + } + else + { + // No valid regular expression + m_comp_err = 1; + } + + 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(const char* s, Match *match, int execute_flags) const +{ + int err = 1; + if (s != NULL && m_comp_err == 0) + { + if (match) + { + err = ::regexec (&m_preg, + s, + match->GetSize(), + match->GetData(), + execute_flags); + } + else + { + err = ::regexec (&m_preg, + s, + 0, + NULL, + execute_flags); + } + } + + 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 (const char* s, uint32_t idx, std::string& match_str) const +{ + if (idx < m_matches.size()) + { + if (m_matches[idx].rm_eo == m_matches[idx].rm_so) + { + // Matched the empty string... + match_str.clear(); + return true; + } + else if (m_matches[idx].rm_eo > m_matches[idx].rm_so) + { + match_str.assign (s + m_matches[idx].rm_so, + m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool +RegularExpression::Match::GetMatchAtIndex (const char* s, uint32_t idx, llvm::StringRef& match_str) const +{ + if (idx < m_matches.size()) + { + 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 = llvm::StringRef (s + m_matches[idx].rm_so, m_matches[idx].rm_eo - m_matches[idx].rm_so); + return true; + } + } + return false; +} + +bool +RegularExpression::Match::GetMatchSpanningIndices (const char* 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 = llvm::StringRef (s + 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. +//---------------------------------------------------------------------- +const char* +RegularExpression::GetText () const +{ + if (m_re.empty()) + return NULL; + return m_re.c_str(); +} + +//---------------------------------------------------------------------- +// 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/source/Core/Scalar.cpp b/source/Core/Scalar.cpp new file mode 100644 index 00000000000..26f74379625 --- /dev/null +++ b/source/Core/Scalar.cpp @@ -0,0 +1,2279 @@ +//===-- Scalar.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Scalar.h" + +#include +#include + +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Host/Endian.h" + +#include "Plugins/Process/Utility/InstructionUtils.h" + +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 exptected + 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 constructor +//---------------------------------------------------------------------- +Scalar::Scalar() : + m_type(e_void), + m_data() +{ +} + +//---------------------------------------------------------------------- +// Scalar copy constructor +//---------------------------------------------------------------------- +Scalar::Scalar(const Scalar& rhs) : + m_type(rhs.m_type), + m_data(rhs.m_data) // TODO: verify that for C++ this will correctly copy the union?? +{ +} + +//Scalar::Scalar(const RegisterValue& reg) : +// m_type(e_void), +// m_data() +//{ +// switch (reg.info.encoding) +// { +// case eEncodingUint: // unsigned integer +// switch (reg.info.byte_size) +// { +// case 1: m_type = e_uint; m_data.uint = reg.value.uint8; break; +// case 2: m_type = e_uint; m_data.uint = reg.value.uint16; break; +// case 4: m_type = e_uint; m_data.uint = reg.value.uint32; break; +// case 8: m_type = e_ulonglong; m_data.ulonglong = reg.value.uint64; break; +// break; +// } +// break; +// +// case eEncodingSint: // signed integer +// switch (reg.info.byte_size) +// { +// case 1: m_type = e_sint; m_data.sint = reg.value.sint8; break; +// case 2: m_type = e_sint; m_data.sint = reg.value.sint16; break; +// case 4: m_type = e_sint; m_data.sint = reg.value.sint32; break; +// case 8: m_type = e_slonglong; m_data.slonglong = reg.value.sint64; break; +// break; +// } +// break; +// +// case eEncodingIEEE754: // float +// switch (reg.info.byte_size) +// { +// case 4: m_type = e_float; m_data.flt = reg.value.float32; break; +// case 8: m_type = e_double; m_data.dbl = reg.value.float64; break; +// break; +// } +// break; +// case eEncodingVector: // vector registers +// break; +// } +//} + +bool +Scalar::GetData (DataExtractor &data, size_t limit_byte_size) const +{ + size_t byte_size = GetByteSize(); + if (byte_size > 0) + { + if (limit_byte_size < byte_size) + { + if (lldb::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... + data.SetData((uint8_t*)&m_data, limit_byte_size, lldb::endian::InlHostByteOrder()); + } + else if (lldb::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 + data.SetData(((uint8_t*)&m_data) + byte_size - limit_byte_size, limit_byte_size, lldb::endian::InlHostByteOrder()); + } + } + else + { + // We want all of the data + data.SetData((uint8_t*)&m_data, byte_size, lldb::endian::InlHostByteOrder()); + } + return true; + } + data.Clear(); + return false; +} + +size_t +Scalar::GetByteSize() const +{ + switch (m_type) + { + case e_void: + break; + case e_sint: return sizeof(m_data.sint); + case e_uint: return sizeof(m_data.uint); + case e_slong: return sizeof(m_data.slong); + case e_ulong: return sizeof(m_data.ulong); + case e_slonglong: return sizeof(m_data.slonglong); + case e_ulonglong: return sizeof(m_data.ulonglong); + case e_float: return sizeof(m_data.flt); + case e_double: return sizeof(m_data.dbl); + case e_long_double: return sizeof(m_data.ldbl); + } + return 0; +} + +bool +Scalar::IsZero() const +{ + switch (m_type) + { + case e_void: + break; + case e_sint: return m_data.sint == 0; + case e_uint: return m_data.uint == 0; + case e_slong: return m_data.slong == 0; + case e_ulong: return m_data.ulong == 0; + case e_slonglong: return m_data.slonglong == 0; + case e_ulonglong: return m_data.ulonglong == 0; + case e_float: return m_data.flt == 0.0f; + case e_double: return m_data.dbl == 0.0; + case e_long_double: return m_data.ldbl == 0.0; + } + 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: s->Printf("%i", m_data.sint); break; + case e_uint: s->Printf("0x%8.8x", m_data.uint); break; + case e_slong: s->Printf("%li", m_data.slong); break; + case e_ulong: s->Printf("0x%8.8lx", m_data.ulong); break; + case e_slonglong: s->Printf("%lli", m_data.slonglong); break; + case e_ulonglong: s->Printf("0x%16.16llx", m_data.ulonglong); break; + case e_float: s->Printf("%f", m_data.flt); break; + case e_double: s->Printf("%g", m_data.dbl); break; + case e_long_double: s->Printf("%Lg", m_data.ldbl); 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_float: return "float"; + case e_double: return "double"; + case e_long_double: return "long double"; + } + return ""; +} + + + +//---------------------------------------------------------------------- +// Scalar copy constructor +//---------------------------------------------------------------------- +Scalar& +Scalar::operator=(const Scalar& rhs) +{ + if (this != &rhs) + { + m_type = rhs.m_type; + ::memcpy (&m_data, &rhs.m_data, sizeof(m_data)); + } + return *this; +} + +Scalar& +Scalar::operator= (const int v) +{ + m_type = e_sint; + m_data.sint = v; + return *this; +} + + +Scalar& +Scalar::operator= (unsigned int v) +{ + m_type = e_uint; + m_data.uint = v; + return *this; +} + +Scalar& +Scalar::operator= (long v) +{ + m_type = e_slong; + m_data.slong = v; + return *this; +} + +Scalar& +Scalar::operator= (unsigned long v) +{ + m_type = e_ulong; + m_data.ulong = v; + return *this; +} + +Scalar& +Scalar::operator= (long long v) +{ + m_type = e_slonglong; + m_data.slonglong = v; + return *this; +} + +Scalar& +Scalar::operator= (unsigned long long v) +{ + m_type = e_ulonglong; + m_data.ulonglong = v; + return *this; +} + +Scalar& +Scalar::operator= (float v) +{ + m_type = e_float; + m_data.flt = v; + return *this; +} + +Scalar& +Scalar::operator= (double v) +{ + m_type = e_double; + m_data.dbl = v; + return *this; +} + +Scalar& +Scalar::operator= (long double v) +{ + m_type = e_long_double; + m_data.ldbl = v; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Scalar::~Scalar() +{ +} + +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_data.uint = m_data.sint; success = true; break; + case e_slong: m_data.slong = m_data.sint; success = true; break; + case e_ulong: m_data.ulong = m_data.sint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.sint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.sint; success = true; break; + case e_float: m_data.flt = m_data.sint; success = true; break; + case e_double: m_data.dbl = m_data.sint; success = true; break; + case e_long_double: m_data.ldbl = m_data.sint; 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_data.slong = m_data.uint; success = true; break; + case e_ulong: m_data.ulong = m_data.uint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.uint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.uint; success = true; break; + case e_float: m_data.flt = m_data.uint; success = true; break; + case e_double: m_data.dbl = m_data.uint; success = true; break; + case e_long_double: m_data.ldbl = m_data.uint; 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_data.ulong = m_data.slong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.slong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slong; success = true; break; + case e_float: m_data.flt = m_data.slong; success = true; break; + case e_double: m_data.dbl = m_data.slong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slong; 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_data.slonglong = m_data.ulong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ulong; success = true; break; + case e_float: m_data.flt = m_data.ulong; success = true; break; + case e_double: m_data.dbl = m_data.ulong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulong; 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_data.ulonglong = m_data.slonglong; success = true; break; + case e_float: m_data.flt = m_data.slonglong; success = true; break; + case e_double: m_data.dbl = m_data.slonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slonglong; 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_float: m_data.flt = m_data.ulonglong; success = true; break; + case e_double: m_data.dbl = m_data.ulonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + 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: break; + case e_float: success = true; break; + case e_double: m_data.dbl = m_data.flt; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; 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_float: break; + case e_double: success = true; break; + case e_long_double: m_data.ldbl = m_data.dbl; 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_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"; + } + 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::Cast(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_data.uint = m_data.sint; success = true; break; + case e_slong: m_data.slong = m_data.sint; success = true; break; + case e_ulong: m_data.ulong = m_data.sint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.sint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.sint; success = true; break; + case e_float: m_data.flt = m_data.sint; success = true; break; + case e_double: m_data.dbl = m_data.sint; success = true; break; + case e_long_double: m_data.ldbl = m_data.sint; success = true; break; + } + break; + + case e_uint: + switch (type) + { + case e_void: + case e_sint: m_data.sint = m_data.uint; success = true; break; + case e_uint: success = true; break; + case e_slong: m_data.slong = m_data.uint; success = true; break; + case e_ulong: m_data.ulong = m_data.uint; success = true; break; + case e_slonglong: m_data.slonglong = m_data.uint; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.uint; success = true; break; + case e_float: m_data.flt = m_data.uint; success = true; break; + case e_double: m_data.dbl = m_data.uint; success = true; break; + case e_long_double: m_data.ldbl = m_data.uint; success = true; break; + } + break; + + case e_slong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.slong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.slong; success = true; break; + case e_slong: success = true; break; + case e_ulong: m_data.ulong = m_data.slong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.slong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slong; success = true; break; + case e_float: m_data.flt = m_data.slong; success = true; break; + case e_double: m_data.dbl = m_data.slong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slong; success = true; break; + } + break; + + case e_ulong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ulong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ulong; success = true; break; + case e_slong: m_data.slong = m_data.ulong; success = true; break; + case e_ulong: success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulong; success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.ulong; success = true; break; + case e_float: m_data.flt = m_data.ulong; success = true; break; + case e_double: m_data.dbl = m_data.ulong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulong; success = true; break; + } + break; + + case e_slonglong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.slonglong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.slonglong; success = true; break; + case e_slong: m_data.slong = m_data.slonglong; success = true; break; + case e_ulong: m_data.ulong = m_data.slonglong; success = true; break; + case e_slonglong: success = true; break; + case e_ulonglong: m_data.ulonglong = m_data.slonglong; success = true; break; + case e_float: m_data.flt = m_data.slonglong; success = true; break; + case e_double: m_data.dbl = m_data.slonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.slonglong; success = true; break; + } + break; + + case e_ulonglong: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ulonglong; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ulonglong; success = true; break; + case e_slong: m_data.slong = m_data.ulonglong; success = true; break; + case e_ulong: m_data.ulong = m_data.ulonglong; success = true; break; + case e_slonglong: m_data.slonglong = m_data.ulonglong; success = true; break; + case e_ulonglong: success = true; break; + case e_float: m_data.flt = m_data.ulonglong; success = true; break; + case e_double: m_data.dbl = m_data.ulonglong; success = true; break; + case e_long_double: m_data.ldbl = m_data.ulonglong; success = true; break; + } + break; + + case e_float: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.flt; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.flt; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.flt; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.flt; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.flt; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.flt; success = true; break; + case e_float: success = true; break; + case e_double: m_data.dbl = m_data.flt; success = true; break; + case e_long_double: m_data.ldbl = m_data.flt; success = true; break; + } + break; + + case e_double: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.dbl; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.dbl; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.dbl; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.dbl; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.dbl; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.dbl; success = true; break; + case e_float: m_data.flt = (float_t)m_data.dbl; success = true; break; + case e_double: success = true; break; + case e_long_double: m_data.ldbl = m_data.dbl; success = true; break; + } + break; + + case e_long_double: + switch (type) + { + case e_void: + case e_sint: m_data.sint = (sint_t)m_data.ldbl; success = true; break; + case e_uint: m_data.uint = (uint_t)m_data.ldbl; success = true; break; + case e_slong: m_data.slong = (slong_t)m_data.ldbl; success = true; break; + case e_ulong: m_data.ulong = (ulong_t)m_data.ldbl; success = true; break; + case e_slonglong: m_data.slonglong = (slonglong_t)m_data.ldbl; success = true; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)m_data.ldbl; success = true; break; + case e_float: m_data.flt = (float_t)m_data.ldbl; success = true; break; + case e_double: m_data.dbl = (double_t)m_data.ldbl; success = true; break; + case e_long_double: success = true; break; + } + break; + } + + if (success) + m_type = type; + return success; +} + +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_float: success = true; break; + case e_double: success = true; break; + case e_long_double: success = true; break; + } + + return success; +} + +int +Scalar::SInt(int fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return m_data.sint; + case e_uint: return (int)m_data.uint; + case e_slong: return (int)m_data.slong; + case e_ulong: return (int)m_data.ulong; + case e_slonglong: return (int)m_data.slonglong; + case e_ulonglong: return (int)m_data.ulonglong; + case e_float: return (int)m_data.flt; + case e_double: return (int)m_data.dbl; + case e_long_double: return (int)m_data.ldbl; + } + return fail_value; +} + +unsigned int +Scalar::UInt(unsigned int fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned int)m_data.sint; + case e_uint: return (unsigned int)m_data.uint; + case e_slong: return (unsigned int)m_data.slong; + case e_ulong: return (unsigned int)m_data.ulong; + case e_slonglong: return (unsigned int)m_data.slonglong; + case e_ulonglong: return (unsigned int)m_data.ulonglong; + case e_float: return (unsigned int)m_data.flt; + case e_double: return (unsigned int)m_data.dbl; + case e_long_double: return (unsigned int)m_data.ldbl; + } + return fail_value; +} + + +long +Scalar::SLong(long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long)m_data.sint; + case e_uint: return (long)m_data.uint; + case e_slong: return (long)m_data.slong; + case e_ulong: return (long)m_data.ulong; + case e_slonglong: return (long)m_data.slonglong; + case e_ulonglong: return (long)m_data.ulonglong; + case e_float: return (long)m_data.flt; + case e_double: return (long)m_data.dbl; + case e_long_double: return (long)m_data.ldbl; + } + return fail_value; +} + + + +unsigned long +Scalar::ULong(unsigned long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned long)m_data.sint; + case e_uint: return (unsigned long)m_data.uint; + case e_slong: return (unsigned long)m_data.slong; + case e_ulong: return (unsigned long)m_data.ulong; + case e_slonglong: return (unsigned long)m_data.slonglong; + case e_ulonglong: return (unsigned long)m_data.ulonglong; + case e_float: return (unsigned long)m_data.flt; + case e_double: return (unsigned long)m_data.dbl; + case e_long_double: return (unsigned long)m_data.ldbl; + } + return fail_value; +} + +uint64_t +Scalar::GetRawBits64(uint64_t fail_value) const +{ + switch (m_type) + { + case e_void: + break; + + case e_sint: + case e_uint: + return m_data.uint; + + case e_slong: + case e_ulong: + return m_data.ulong; + + case e_slonglong: + case e_ulonglong: + return m_data.ulonglong; + + case e_float: + if (sizeof(m_data.flt) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.flt) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.flt) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.dbl) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.dbl) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(m_data.uint)) + return m_data.uint; + else if (sizeof(m_data.ldbl) == sizeof(m_data.ulong)) + return m_data.ulong; + else if (sizeof(m_data.ldbl) == sizeof(m_data.ulonglong)) + return m_data.ulonglong; + break; + } + return fail_value; +} + + + +long long +Scalar::SLongLong(long long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long long)m_data.sint; + case e_uint: return (long long)m_data.uint; + case e_slong: return (long long)m_data.slong; + case e_ulong: return (long long)m_data.ulong; + case e_slonglong: return (long long)m_data.slonglong; + case e_ulonglong: return (long long)m_data.ulonglong; + case e_float: return (long long)m_data.flt; + case e_double: return (long long)m_data.dbl; + case e_long_double: return (long long)m_data.ldbl; + } + return fail_value; +} + + +unsigned long long +Scalar::ULongLong(unsigned long long fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (unsigned long long)m_data.sint; + case e_uint: return (unsigned long long)m_data.uint; + case e_slong: return (unsigned long long)m_data.slong; + case e_ulong: return (unsigned long long)m_data.ulong; + case e_slonglong: return (unsigned long long)m_data.slonglong; + case e_ulonglong: return (unsigned long long)m_data.ulonglong; + case e_float: return (unsigned long long)m_data.flt; + case e_double: return (unsigned long long)m_data.dbl; + case e_long_double: return (unsigned long long)m_data.ldbl; + } + return fail_value; +} + + +float +Scalar::Float(float fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (float)m_data.sint; + case e_uint: return (float)m_data.uint; + case e_slong: return (float)m_data.slong; + case e_ulong: return (float)m_data.ulong; + case e_slonglong: return (float)m_data.slonglong; + case e_ulonglong: return (float)m_data.ulonglong; + case e_float: return (float)m_data.flt; + case e_double: return (float)m_data.dbl; + case e_long_double: return (float)m_data.ldbl; + } + return fail_value; +} + + +double +Scalar::Double(double fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (double)m_data.sint; + case e_uint: return (double)m_data.uint; + case e_slong: return (double)m_data.slong; + case e_ulong: return (double)m_data.ulong; + case e_slonglong: return (double)m_data.slonglong; + case e_ulonglong: return (double)m_data.ulonglong; + case e_float: return (double)m_data.flt; + case e_double: return (double)m_data.dbl; + case e_long_double: return (double)m_data.ldbl; + } + return fail_value; +} + + +long double +Scalar::LongDouble(long double fail_value) const +{ + switch (m_type) + { + case e_void: break; + case e_sint: return (long double)m_data.sint; + case e_uint: return (long double)m_data.uint; + case e_slong: return (long double)m_data.slong; + case e_ulong: return (long double)m_data.ulong; + case e_slonglong: return (long double)m_data.slonglong; + case e_ulonglong: return (long double)m_data.ulonglong; + case e_float: return (long double)m_data.flt; + case e_double: return (long double)m_data.dbl; + case e_long_double: return (long double)m_data.ldbl; + } + 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: m_data.sint = a->m_data.sint + b->m_data.sint; break; + case e_uint: m_data.uint = a->m_data.uint + b->m_data.uint; break; + case e_slong: m_data.slong = a->m_data.slong + b->m_data.slong; break; + case e_ulong: m_data.ulong = a->m_data.ulong + b->m_data.ulong; break; + case e_slonglong: m_data.slonglong = a->m_data.slonglong + b->m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong = a->m_data.ulonglong + b->m_data.ulonglong; break; + case e_float: m_data.flt = a->m_data.flt + b->m_data.flt; break; + case e_double: m_data.dbl = a->m_data.dbl + b->m_data.dbl; break; + case e_long_double: m_data.ldbl = a->m_data.ldbl + b->m_data.ldbl; 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: + 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: m_data.sint <<= rhs.m_data.sint; break; + case e_uint: m_data.sint <<= rhs.m_data.uint; break; + case e_slong: m_data.sint <<= rhs.m_data.slong; break; + case e_ulong: m_data.sint <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint <<= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + 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: m_data.uint <<= rhs.m_data.sint; break; + case e_uint: m_data.uint <<= rhs.m_data.uint; break; + case e_slong: m_data.uint <<= rhs.m_data.slong; break; + case e_ulong: m_data.uint <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint <<= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + 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: m_data.slong <<= rhs.m_data.sint; break; + case e_uint: m_data.slong <<= rhs.m_data.uint; break; + case e_slong: m_data.slong <<= rhs.m_data.slong; break; + case e_ulong: m_data.slong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong <<= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + 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: m_data.ulong <<= rhs.m_data.sint; break; + case e_uint: m_data.ulong <<= rhs.m_data.uint; break; + case e_slong: m_data.ulong <<= rhs.m_data.slong; break; + case e_ulong: m_data.ulong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong <<= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + 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: m_data.slonglong <<= rhs.m_data.sint; break; + case e_uint: m_data.slonglong <<= rhs.m_data.uint; break; + case e_slong: m_data.slonglong <<= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong <<= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + 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: m_data.ulonglong <<= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong <<= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong <<= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong <<= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong <<= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong <<= rhs.m_data.ulonglong; 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: + 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: m_data.uint >>= rhs.m_data.sint; break; + case e_uint: m_data.uint >>= rhs.m_data.uint; break; + case e_slong: m_data.uint >>= rhs.m_data.slong; break; + case e_ulong: m_data.uint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + case e_ulong: + 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: m_data.ulong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slonglong: + case e_ulonglong: + 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: m_data.ulonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong >>= rhs.m_data.ulonglong; 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: + 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: m_data.sint >>= rhs.m_data.sint; break; + case e_uint: m_data.sint >>= rhs.m_data.uint; break; + case e_slong: m_data.sint >>= rhs.m_data.slong; break; + case e_ulong: m_data.sint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + 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: m_data.uint >>= rhs.m_data.sint; break; + case e_uint: m_data.uint >>= rhs.m_data.uint; break; + case e_slong: m_data.uint >>= rhs.m_data.slong; break; + case e_ulong: m_data.uint >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint >>= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + 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: m_data.slong >>= rhs.m_data.sint; break; + case e_uint: m_data.slong >>= rhs.m_data.uint; break; + case e_slong: m_data.slong >>= rhs.m_data.slong; break; + case e_ulong: m_data.slong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + 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: m_data.ulong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong >>= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + 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: m_data.slonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.slonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.slonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong >>= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + 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: m_data.ulonglong >>= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong >>= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong >>= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong >>= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong >>= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong >>= rhs.m_data.ulonglong; 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: + 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: m_data.sint &= rhs.m_data.sint; break; + case e_uint: m_data.sint &= rhs.m_data.uint; break; + case e_slong: m_data.sint &= rhs.m_data.slong; break; + case e_ulong: m_data.sint &= rhs.m_data.ulong; break; + case e_slonglong: m_data.sint &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.sint &= rhs.m_data.ulonglong; break; + } + break; + + case e_uint: + 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: m_data.uint &= rhs.m_data.sint; break; + case e_uint: m_data.uint &= rhs.m_data.uint; break; + case e_slong: m_data.uint &= rhs.m_data.slong; break; + case e_ulong: m_data.uint &= rhs.m_data.ulong; break; + case e_slonglong: m_data.uint &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.uint &= rhs.m_data.ulonglong; break; + } + break; + + case e_slong: + 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: m_data.slong &= rhs.m_data.sint; break; + case e_uint: m_data.slong &= rhs.m_data.uint; break; + case e_slong: m_data.slong &= rhs.m_data.slong; break; + case e_ulong: m_data.slong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.slong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slong &= rhs.m_data.ulonglong; break; + } + break; + + case e_ulong: + 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: m_data.ulong &= rhs.m_data.sint; break; + case e_uint: m_data.ulong &= rhs.m_data.uint; break; + case e_slong: m_data.ulong &= rhs.m_data.slong; break; + case e_ulong: m_data.ulong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulong &= rhs.m_data.ulonglong; break; + } + break; + case e_slonglong: + 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: m_data.slonglong &= rhs.m_data.sint; break; + case e_uint: m_data.slonglong &= rhs.m_data.uint; break; + case e_slong: m_data.slonglong &= rhs.m_data.slong; break; + case e_ulong: m_data.slonglong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.slonglong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.slonglong &= rhs.m_data.ulonglong; break; + } + break; + + case e_ulonglong: + 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: m_data.ulonglong &= rhs.m_data.sint; break; + case e_uint: m_data.ulonglong &= rhs.m_data.uint; break; + case e_slong: m_data.ulonglong &= rhs.m_data.slong; break; + case e_ulong: m_data.ulonglong &= rhs.m_data.ulong; break; + case e_slonglong: m_data.ulonglong &= rhs.m_data.slonglong; break; + case e_ulonglong: m_data.ulonglong &= rhs.m_data.ulonglong; break; + } + break; + } + return *this; +} + + + +bool +Scalar::AbsoluteValue() +{ + switch (m_type) + { + case e_void: + break; + + case e_sint: + if (m_data.sint < 0) + m_data.sint = -m_data.sint; + return true; + + case e_slong: + if (m_data.slong < 0) + m_data.slong = -m_data.slong; + return true; + + case e_slonglong: + if (m_data.slonglong < 0) + m_data.slonglong = -m_data.slonglong; + return true; + + case e_uint: + case e_ulong: + case e_ulonglong: return true; + case e_float: m_data.flt = fabsf(m_data.flt); return true; + case e_double: m_data.dbl = fabs(m_data.dbl); return true; + case e_long_double: m_data.ldbl = fabsl(m_data.ldbl); return true; + } + return false; +} + + +bool +Scalar::UnaryNegate() +{ + switch (m_type) + { + case e_void: break; + case e_sint: m_data.sint = -m_data.sint; return true; + case e_uint: m_data.uint = -m_data.uint; return true; + case e_slong: m_data.slong = -m_data.slong; return true; + case e_ulong: m_data.ulong = -m_data.ulong; return true; + case e_slonglong: m_data.slonglong = -m_data.slonglong; return true; + case e_ulonglong: m_data.ulonglong = -m_data.ulonglong; return true; + case e_float: m_data.flt = -m_data.flt; return true; + case e_double: m_data.dbl = -m_data.dbl; return true; + case e_long_double: m_data.ldbl = -m_data.ldbl; return true; + } + return false; +} + +bool +Scalar::OnesComplement() +{ + switch (m_type) + { + case e_sint: m_data.sint = ~m_data.sint; return true; + case e_uint: m_data.uint = ~m_data.uint; return true; + case e_slong: m_data.slong = ~m_data.slong; return true; + case e_ulong: m_data.ulong = ~m_data.ulong; return true; + case e_slonglong: m_data.slonglong = ~m_data.slonglong; return true; + case e_ulonglong: m_data.ulonglong = ~m_data.ulonglong; 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: result.m_data.sint = a->m_data.sint + b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint + b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong + b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong + b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong + b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong + b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt + b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl + b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl + b->m_data.ldbl; 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: result.m_data.sint = a->m_data.sint - b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint - b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong - b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong - b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong - b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong - b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt - b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl - b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl - b->m_data.ldbl; 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: if (b->m_data.sint != 0) { result.m_data.sint = a->m_data.sint/ b->m_data.sint; return result; } break; + case Scalar::e_uint: if (b->m_data.uint != 0) { result.m_data.uint = a->m_data.uint / b->m_data.uint; return result; } break; + case Scalar::e_slong: if (b->m_data.slong != 0) { result.m_data.slong = a->m_data.slong / b->m_data.slong; return result; } break; + case Scalar::e_ulong: if (b->m_data.ulong != 0) { result.m_data.ulong = a->m_data.ulong / b->m_data.ulong; return result; } break; + case Scalar::e_slonglong: if (b->m_data.slonglong != 0) { result.m_data.slonglong = a->m_data.slonglong / b->m_data.slonglong; return result; } break; + case Scalar::e_ulonglong: if (b->m_data.ulonglong != 0) { result.m_data.ulonglong = a->m_data.ulonglong / b->m_data.ulonglong; return result; } break; + case Scalar::e_float: if (b->m_data.flt != 0.0f) { result.m_data.flt = a->m_data.flt / b->m_data.flt; return result; } break; + case Scalar::e_double: if (b->m_data.dbl != 0.0) { result.m_data.dbl = a->m_data.dbl / b->m_data.dbl; return result; } break; + case Scalar::e_long_double: if (b->m_data.ldbl != 0.0) { result.m_data.ldbl = a->m_data.ldbl / b->m_data.ldbl; 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: result.m_data.sint = a->m_data.sint * b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint * b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong * b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong * b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong * b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong * b->m_data.ulonglong; break; + case Scalar::e_float: result.m_data.flt = a->m_data.flt * b->m_data.flt; break; + case Scalar::e_double: result.m_data.dbl = a->m_data.dbl * b->m_data.dbl; break; + case Scalar::e_long_double: result.m_data.ldbl = a->m_data.ldbl * b->m_data.ldbl; 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: result.m_data.sint = a->m_data.sint & b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint & b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong & b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong & b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong & b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong & b->m_data.ulonglong; 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: result.m_data.sint = a->m_data.sint | b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint | b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong | b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong | b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong | b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong | b->m_data.ulonglong; 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_sint: if (b->m_data.sint != 0) { result.m_data.sint = a->m_data.sint % b->m_data.sint; return result; } break; + case Scalar::e_uint: if (b->m_data.uint != 0) { result.m_data.uint = a->m_data.uint % b->m_data.uint; return result; } break; + case Scalar::e_slong: if (b->m_data.slong != 0) { result.m_data.slong = a->m_data.slong % b->m_data.slong; return result; } break; + case Scalar::e_ulong: if (b->m_data.ulong != 0) { result.m_data.ulong = a->m_data.ulong % b->m_data.ulong; return result; } break; + case Scalar::e_slonglong: if (b->m_data.slonglong != 0) { result.m_data.slonglong = a->m_data.slonglong % b->m_data.slonglong; return result; } break; + case Scalar::e_ulonglong: if (b->m_data.ulonglong != 0) { result.m_data.ulonglong = a->m_data.ulonglong % b->m_data.ulonglong; 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: result.m_data.sint = a->m_data.sint ^ b->m_data.sint; break; + case Scalar::e_uint: result.m_data.uint = a->m_data.uint ^ b->m_data.uint; break; + case Scalar::e_slong: result.m_data.slong = a->m_data.slong ^ b->m_data.slong; break; + case Scalar::e_ulong: result.m_data.ulong = a->m_data.ulong ^ b->m_data.ulong; break; + case Scalar::e_slonglong: result.m_data.slonglong = a->m_data.slonglong ^ b->m_data.slonglong; break; + case Scalar::e_ulonglong: result.m_data.ulonglong = a->m_data.ulonglong ^ b->m_data.ulonglong; 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; +} + +// Return the raw unsigned integer without any casting or conversion +unsigned int +Scalar::RawUInt () const +{ + return m_data.uint; +} + +// Return the raw unsigned long without any casting or conversion +unsigned long +Scalar::RawULong () const +{ + return m_data.ulong; +} + +// Return the raw unsigned long long without any casting or conversion +unsigned long long +Scalar::RawULongLong () const +{ + return m_data.ulonglong; +} + + +Error +Scalar::SetValueFromCString (const char *value_str, Encoding encoding, size_t byte_size) +{ + Error error; + if (value_str == NULL || value_str[0] == '\0') + { + error.SetErrorString ("Invalid c-string value string."); + return error; + } + bool success = false; + switch (encoding) + { + case eEncodingInvalid: + error.SetErrorString ("Invalid encoding."); + break; + + case eEncodingUint: + if (byte_size <= sizeof (unsigned long long)) + { + uint64_t uval64 = Args::StringToUInt64(value_str, UINT64_MAX, 0, &success); + if (!success) + 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 %zu byte unsigned integer value", uval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForUnsignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_uint: m_data.uint = (uint_t)uval64; break; + case e_ulong: m_data.ulong = (ulong_t)uval64; break; + case e_ulonglong: m_data.ulonglong = (ulonglong_t)uval64; break; + default: + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingSint: + if (byte_size <= sizeof (long long)) + { + uint64_t sval64 = Args::StringToSInt64(value_str, INT64_MAX, 0, &success); + if (!success) + 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 %zu byte signed integer value", sval64, byte_size); + else + { + m_type = Scalar::GetValueTypeForSignedIntegerWithByteSize (byte_size); + switch (m_type) + { + case e_sint: m_data.sint = (sint_t)sval64; break; + case e_slong: m_data.slong = (slong_t)sval64; break; + case e_slonglong: m_data.slonglong = (slonglong_t)sval64; break; + default: + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + break; + } + } + } + else + { + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + return error; + } + break; + + case eEncodingIEEE754: + if (byte_size == sizeof (float)) + { + if (::sscanf (value_str, "%f", &m_data.flt) == 1) + 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", &m_data.dbl) == 1) + 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", &m_data.ldbl) == 1) + 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: %zu", byte_size); + return error; + } + break; + + case eEncodingVector: + error.SetErrorString ("vector encoding unsupported."); + break; + } + if (error.Fail()) + m_type = e_void; + + return error; +} + +Error +Scalar::SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t byte_size) +{ + Error error; + + 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; + + switch (byte_size) + { + case 1: operator=((uint8_t)data.GetU8(&offset)); break; + case 2: operator=((uint16_t)data.GetU16(&offset)); break; + case 4: operator=((uint32_t)data.GetU32(&offset)); break; + case 8: operator=((uint64_t)data.GetU64(&offset)); break; + default: + error.SetErrorStringWithFormat ("unsupported unsigned integer byte size: %zu", byte_size); + break; + } + } + break; + case lldb::eEncodingSint: + { + lldb::offset_t offset; + + switch (byte_size) + { + case 1: operator=((int8_t)data.GetU8(&offset)); break; + case 2: operator=((int16_t)data.GetU16(&offset)); break; + case 4: operator=((int32_t)data.GetU32(&offset)); break; + case 8: operator=((int64_t)data.GetU64(&offset)); break; + default: + error.SetErrorStringWithFormat ("unsupported signed integer byte size: %zu", byte_size); + break; + } + } + break; + case lldb::eEncodingIEEE754: + { + lldb::offset_t offset; + + if (byte_size == sizeof (float)) + operator=((float)data.GetFloat(&offset)); + else if (byte_size == sizeof (double)) + operator=((double)data.GetDouble(&offset)); + else if (byte_size == sizeof (long double)) + operator=((long double)data.GetLongDouble(&offset)); + else + error.SetErrorStringWithFormat ("unsupported float byte size: %zu", 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: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned int sign_bit = 1u << sign_bit_pos; + if (m_data.uint & sign_bit) + { + const unsigned int mask = ~(sign_bit) + 1u; + m_data.uint |= mask; + } + return true; + } + break; + + case Scalar::e_slong: + case Scalar::e_ulong: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned long sign_bit = 1ul << sign_bit_pos; + if (m_data.ulong & sign_bit) + { + const unsigned long mask = ~(sign_bit) + 1ul; + m_data.ulong |= mask; + } + return true; + } + break; + + case Scalar::e_slonglong: + case Scalar::e_ulonglong: + if (max_bit_pos == sign_bit_pos) + return true; + else if (sign_bit_pos < (max_bit_pos-1)) + { + unsigned long long sign_bit = 1ull << sign_bit_pos; + if (m_data.ulonglong & sign_bit) + { + const unsigned long long mask = ~(sign_bit) + 1ull; + m_data.ulonglong |= mask; + } + return true; + } + break; + } + } + return false; +} + +size_t +Scalar::GetAsMemoryData (void *dst, + size_t dst_len, + lldb::ByteOrder dst_byte_order, + Error &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; + + uint32_t msbit = bit_offset + bit_size - 1; + uint32_t lsbit = bit_offset; + switch (m_type) + { + case Scalar::e_void: + break; + + case e_float: + if (sizeof(m_data.flt) == sizeof(sint_t)) + m_data.sint = (sint_t)SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.flt) == sizeof(ulong_t)) + m_data.slong = (slong_t)SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.flt) == sizeof(ulonglong_t)) + m_data.slonglong = (slonglong_t)SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case e_double: + if (sizeof(m_data.dbl) == sizeof(sint_t)) + m_data.sint = SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.dbl) == sizeof(ulong_t)) + m_data.slong = SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.dbl) == sizeof(ulonglong_t)) + m_data.slonglong = SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case e_long_double: + if (sizeof(m_data.ldbl) == sizeof(sint_t)) + m_data.sint = SignedBits (m_data.sint, msbit, lsbit); + else if (sizeof(m_data.ldbl) == sizeof(ulong_t)) + m_data.slong = SignedBits (m_data.slong, msbit, lsbit); + else if (sizeof(m_data.ldbl) == sizeof(ulonglong_t)) + m_data.slonglong = SignedBits (m_data.slonglong, msbit, lsbit); + else + return false; + return true; + + case Scalar::e_sint: + m_data.sint = (sint_t)SignedBits (m_data.sint, msbit, lsbit); + return true; + + case Scalar::e_uint: + m_data.uint = (uint_t)UnsignedBits (m_data.uint, msbit, lsbit); + return true; + + case Scalar::e_slong: + m_data.slong = (slong_t)SignedBits (m_data.slong, msbit, lsbit); + return true; + + case Scalar::e_ulong: + m_data.ulong = (ulong_t)UnsignedBits (m_data.ulong, msbit, lsbit); + return true; + + case Scalar::e_slonglong: + m_data.slonglong = (slonglong_t)SignedBits (m_data.slonglong, msbit, lsbit); + return true; + + case Scalar::e_ulonglong: + m_data.ulonglong = (ulonglong_t)UnsignedBits (m_data.ulonglong, msbit, lsbit); + 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; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint == b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint == b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong == b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong == b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong == b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong == b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt == b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl == b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl == b->m_data.ldbl; + } + 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; // A temp value that might get a copy of either promoted value + const Scalar* a; + const Scalar* b; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint != b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint != b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong != b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong != b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong != b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong != b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt != b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl != b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl != b->m_data.ldbl; + } + return true; +} + +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; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint < b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint < b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong < b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong < b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong < b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong < b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt < b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl < b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl < b->m_data.ldbl; + } + return false; +} + +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; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint <= b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint <= b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong <= b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong <= b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong <= b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong <= b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt <= b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl <= b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl <= b->m_data.ldbl; + } + return false; +} + + +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; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint > b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint > b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong > b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong > b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong > b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong > b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt > b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl > b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl > b->m_data.ldbl; + } + return false; +} + +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; + switch (PromoteToMaxType(lhs, rhs, temp_value, a, b)) + { + case Scalar::e_void: break; + case Scalar::e_sint: return a->m_data.sint >= b->m_data.sint; + case Scalar::e_uint: return a->m_data.uint >= b->m_data.uint; + case Scalar::e_slong: return a->m_data.slong >= b->m_data.slong; + case Scalar::e_ulong: return a->m_data.ulong >= b->m_data.ulong; + case Scalar::e_slonglong: return a->m_data.slonglong >= b->m_data.slonglong; + case Scalar::e_ulonglong: return a->m_data.ulonglong >= b->m_data.ulonglong; + case Scalar::e_float: return a->m_data.flt >= b->m_data.flt; + case Scalar::e_double: return a->m_data.dbl >= b->m_data.dbl; + case Scalar::e_long_double: return a->m_data.ldbl >= b->m_data.ldbl; + } + return false; +} + + + + diff --git a/source/Core/SearchFilter.cpp b/source/Core/SearchFilter.cpp new file mode 100644 index 00000000000..54937c0afec --- /dev/null +++ b/source/Core/SearchFilter.cpp @@ -0,0 +1,816 @@ +//===-- SearchFilter.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/lldb-private.h" +#include "lldb/Core/SearchFilter.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// SearchFilter constructor +//---------------------------------------------------------------------- +Searcher::Searcher () +{ + +} + +Searcher::~Searcher () +{ + +} + +void +Searcher::GetDescription (Stream *s) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter constructor +//---------------------------------------------------------------------- +SearchFilter::SearchFilter(const TargetSP &target_sp) : + m_target_sp (target_sp) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter copy constructor +//---------------------------------------------------------------------- +SearchFilter::SearchFilter(const SearchFilter& rhs) : + m_target_sp (rhs.m_target_sp) +{ +} + +//---------------------------------------------------------------------- +// SearchFilter assignment operator +//---------------------------------------------------------------------- +const SearchFilter& +SearchFilter::operator=(const SearchFilter& rhs) +{ + m_target_sp = rhs.m_target_sp; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilter::~SearchFilter() +{ +} + +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; +} + +uint32_t +SearchFilter::GetFilterRequiredItems() +{ + return (lldb::SymbolContextItem) 0; +} + +void +SearchFilter::GetDescription (Stream *s) +{ +} + +void +SearchFilter::Dump (Stream *s) const +{ + +} + +//---------------------------------------------------------------------- +// 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() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, 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() == Searcher::eDepthTarget) + searcher.SearchCallback (*this, empty_sc, NULL, false); + else + { + Mutex::Locker modules_locker(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 () >= Searcher::eDepthModule) + { + if (context.module_sp) + { + if (searcher.GetDepth () == Searcher::eDepthModule) + { + SymbolContext matchingContext(context.module_sp.get()); + searcher.SearchCallback (*this, matchingContext, NULL, false); + } + else + { + return DoCUIteration(context.module_sp, context, searcher); + } + } + else + { + const ModuleList &target_images = m_target_sp->GetImages(); + Mutex::Locker modules_locker(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 () == Searcher::eDepthModule) + { + SymbolContext matchingContext(m_target_sp, module_sp); + + Searcher::CallbackReturn shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, 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 == NULL) + { + 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 () == Searcher::eDepthCompUnit) + { + SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get()); + + shouldContinue = searcher.SearchCallback (*this, matchingContext, NULL, false); + + if (shouldContinue == Searcher::eCallbackReturnPop) + return Searcher::eCallbackReturnContinue; + else if (shouldContinue == Searcher::eCallbackReturnStop) + return shouldContinue; + } + else + { + // FIXME Descend to block. + } + } + } + } + else + { + if (CompUnitPasses(*context.comp_unit)) + { + SymbolContext matchingContext (m_target_sp, module_sp, context.comp_unit); + return searcher.SearchCallback (*this, matchingContext, NULL, false); + } + } + return Searcher::eCallbackReturnContinue; +} + +Searcher::CallbackReturn +SearchFilter::DoFunctionIteration (Function *function, const SymbolContext &context, Searcher &searcher) +{ + // FIXME: Implement... + return Searcher::eCallbackReturnContinue; +} + +//---------------------------------------------------------------------- +// SearchFilterForNonModuleSpecificSearches: +// Selects a shared library matching a given file spec, consulting the targets "black list". +//---------------------------------------------------------------------- + + bool + SearchFilterForNonModuleSpecificSearches::ModulePasses (const FileSpec &module_spec) + { + if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_spec)) + return false; + else + return true; + } + + bool + SearchFilterForNonModuleSpecificSearches::ModulePasses (const lldb::ModuleSP &module_sp) + { + if (!module_sp) + return true; + else if (m_target_sp->ModuleIsExcludedForNonModuleSpecificSearches (module_sp)) + return false; + else + return true; + } + +//---------------------------------------------------------------------- +// SearchFilterByModule: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModule constructors +//---------------------------------------------------------------------- + +SearchFilterByModule::SearchFilterByModule (const lldb::TargetSP &target_sp, const FileSpec &module) : + SearchFilter (target_sp), + m_module_spec (module) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModule copy constructor +//---------------------------------------------------------------------- +SearchFilterByModule::SearchFilterByModule(const SearchFilterByModule& rhs) : + SearchFilter (rhs), + m_module_spec (rhs.m_module_spec) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModule assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModule& +SearchFilterByModule::operator=(const SearchFilterByModule& rhs) +{ + m_target_sp = rhs.m_target_sp; + m_module_spec = rhs.m_module_spec; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModule::~SearchFilterByModule() +{ +} + +bool +SearchFilterByModule::ModulePasses (const ModuleSP &module_sp) +{ + if (module_sp && FileSpec::Equal(module_sp->GetFileSpec(), m_module_spec, false)) + return true; + else + return false; +} + +bool +SearchFilterByModule::ModulePasses (const FileSpec &spec) +{ + // Do a full match only if "spec" has a directory + const bool full_match = 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() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, 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(); + Mutex::Locker modules_locker (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 = 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 = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec.GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec.GetFilename().AsCString("")); + } +} + +uint32_t +SearchFilterByModule::GetFilterRequiredItems() +{ + return eSymbolContextModule; +} + +void +SearchFilterByModule::Dump (Stream *s) const +{ + +} +//---------------------------------------------------------------------- +// SearchFilterByModuleList: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModuleList constructors +//---------------------------------------------------------------------- + +SearchFilterByModuleList::SearchFilterByModuleList (const lldb::TargetSP &target_sp, const FileSpecList &module_list) : + SearchFilter (target_sp), + m_module_spec_list (module_list) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModuleList copy constructor +//---------------------------------------------------------------------- +SearchFilterByModuleList::SearchFilterByModuleList(const SearchFilterByModuleList& rhs) : + SearchFilter (rhs), + m_module_spec_list (rhs.m_module_spec_list) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleList assignment operator +//---------------------------------------------------------------------- +const SearchFilterByModuleList& +SearchFilterByModuleList::operator=(const SearchFilterByModuleList& rhs) +{ + m_target_sp = rhs.m_target_sp; + m_module_spec_list = rhs.m_module_spec_list; + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModuleList::~SearchFilterByModuleList() +{ +} + +bool +SearchFilterByModuleList::ModulePasses (const ModuleSP &module_sp) +{ + if (m_module_spec_list.GetSize() == 0) + return true; + + if (module_sp && m_module_spec_list.FindFileIndex(0, module_sp->GetFileSpec(), false) != UINT32_MAX) + return true; + else + return false; +} + +bool +SearchFilterByModuleList::ModulePasses (const FileSpec &spec) +{ + if (m_module_spec_list.GetSize() == 0) + return true; + + if (m_module_spec_list.FindFileIndex(0, spec, true) != UINT32_MAX) + return true; + else + return false; +} + +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() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, 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(); + Mutex::Locker modules_locker (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 = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(0).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("")); + } + } + else + { + s->Printf (", modules(%zu) = ", num_modules); + for (size_t i = 0; i < num_modules; i++) + { + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(i).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("")); + } + if (i != num_modules - 1) + s->PutCString (", "); + } + } +} + +uint32_t +SearchFilterByModuleList::GetFilterRequiredItems() +{ + return eSymbolContextModule; +} + +void +SearchFilterByModuleList::Dump (Stream *s) const +{ + +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU: +// Selects a shared library matching a given file spec +//---------------------------------------------------------------------- + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU constructors +//---------------------------------------------------------------------- + +SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU (const lldb::TargetSP &target_sp, + const FileSpecList &module_list, + const FileSpecList &cu_list) : + SearchFilterByModuleList (target_sp, module_list), + m_cu_spec_list (cu_list) +{ +} + + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU copy constructor +//---------------------------------------------------------------------- +SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU(const SearchFilterByModuleListAndCU& rhs) : + SearchFilterByModuleList (rhs), + m_cu_spec_list (rhs.m_cu_spec_list) +{ +} + +//---------------------------------------------------------------------- +// SearchFilterByModuleListAndCU assignment operator +//---------------------------------------------------------------------- +const 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; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() +{ +} + +bool +SearchFilterByModuleListAndCU::AddressPasses (Address &address) +{ + return true; +} + + +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() == Searcher::eDepthTarget) + { + SymbolContext empty_sc; + empty_sc.target_sp = m_target_sp; + searcher.SearchCallback (*this, empty_sc, NULL, 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(); + Mutex::Locker modules_locker(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() == Searcher::eDepthModule) + { + 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 = "); + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(0).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString("")); + } + } + else if (num_modules > 0) + { + s->Printf (", modules(%zd) = ", num_modules); + for (size_t i = 0; i < num_modules; i++) + { + if (s->GetVerbose()) + { + char buffer[2048]; + m_module_spec_list.GetFileSpecAtIndex(i).GetPath(buffer, 2047); + s->PutCString(buffer); + } + else + { + s->PutCString(m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString("")); + } + if (i != num_modules - 1) + s->PutCString (", "); + } + } +} + +uint32_t +SearchFilterByModuleListAndCU::GetFilterRequiredItems() +{ + return eSymbolContextModule | eSymbolContextCompUnit; +} + +void +SearchFilterByModuleListAndCU::Dump (Stream *s) const +{ + +} + diff --git a/source/Core/Section.cpp b/source/Core/Section.cpp new file mode 100644 index 00000000000..e2a084ceb2f --- /dev/null +++ b/source/Core/Section.cpp @@ -0,0 +1,562 @@ +//===-- Section.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Section.h" +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +Section::Section (const ModuleSP &module_sp, + ObjectFile *obj_file, + user_id_t sect_id, + const 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 flags) : + 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_children (), + m_fake (false), + m_encrypted (false), + m_thread_specific (false) +{ +// 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, + const 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 flags) : + 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_children (), + m_fake (false), + m_encrypted (false), + m_thread_specific (false) +{ +// 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(); + } + else + { + load_base_addr = target->GetSectionLoadList().GetSectionLoadAddress (const_cast
(this)->shared_from_this()); + } + return load_base_addr; +} + +bool +Section::ResolveContainedAddress (addr_t offset, Address &so_addr) const +{ + const size_t num_children = m_children.GetSize(); + if (num_children > 0) + { + for (size_t i=0; iGetOffset(); + if (child_offset <= offset && offset - child_offset < child_section->GetByteSize()) + return child_section->ResolveContainedAddress (offset - child_offset, so_addr); + } + } + so_addr.SetOffset(offset); + so_addr.SetSection(const_cast
(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; + 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(), GetSectionTypeAsCString (m_type)); + 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 0x%8.8" PRIx64 " 0x%8.8" PRIx64 " 0x%8.8x ", resolved ? ' ' : '*', 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 = NULL; + ModuleSP module_sp (GetModule()); + const FileSpec &file_spec = m_obj_file->GetFileSpec(); + + if (m_obj_file) + 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; +} + +#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) +{ + assert (section_sp.get()); + size_t section_index = m_sections.size(); + m_sections.push_back(section_sp); + return section_index; +} + +// 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 (const ConstString §ion_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() == NULL; ++sect_iter) + { + Section *child_section = sect_iter->get(); + assert (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() == NULL; ++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() == NULL; ++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() == NULL && !sect->IsFake()) + sect_sp = *sect_iter; + } + } + return sect_sp; +} + +bool +SectionList::ContainsSection(user_id_t sect_id) const +{ + return FindSectionByID (sect_id).get() != NULL; +} + +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 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 : NULL, 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/source/Core/SourceManager.cpp b/source/Core/SourceManager.cpp new file mode 100644 index 00000000000..9f289348fd9 --- /dev/null +++ b/source/Core/SourceManager.cpp @@ -0,0 +1,651 @@ +//===-- SourceManager.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/SourceManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/Target.h" + +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(); + + // If file_sp is no good or it points to a non-existent file, reset it. + if (!file_sp || !file_sp->GetFileSpec().Exists()) + { + file_sp.reset (new File (file_spec, target_sp.get())); + + if (debugger_sp) + debugger_sp->GetSourceFileCache().AddSourceFile(file_sp); + } + return file_sp; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbersUsingLastFile (uint32_t start_line, + uint32_t count, + uint32_t curr_line, + const char* current_line_cstr, + Stream *s, + const SymbolContextList *bp_locs) +{ + if (count == 0) + return 0; + size_t return_value = 0; + 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), " "); + } + + return_value += s->Printf("%s%2.2s %-4u\t", + prefix, + line == curr_line ? current_line_cstr : "", + line); + size_t this_line_size = m_last_file_sp->DisplaySourceLines (line, 0, 0, s); + if (this_line_size == 0) + { + m_last_line = UINT32_MAX; + break; + } + else + return_value += this_line_size; + } + } + return return_value; +} + +size_t +SourceManager::DisplaySourceLinesWithLineNumbers +( + const FileSpec &file_spec, + uint32_t line, + 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, 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; + + return DisplaySourceLinesWithLineNumbersUsingLastFile (m_last_line, m_last_count, UINT32_MAX, "", 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, + NULL, + 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 &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, Target *target) : + m_file_spec_orig (file_spec), + m_file_spec(file_spec), + m_mod_time (file_spec.GetModificationTime()), + m_source_map_mod_id (0), + m_data_sp(), + m_offsets() +{ + if (!m_mod_time.IsValid()) + { + 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, + lldb::eSymbolContextModule | lldb::eSymbolContextCompUnit, + sc_list); + bool got_multiple = false; + if (num_matches != 0) + { + if (num_matches > 1) + { + SymbolContext sc; + FileSpec *test_cu_spec = NULL; + + 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 (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 = m_file_spec.GetModificationTime(); + } + } + } + // Try remapping if m_file_spec does not correspond to an existing file. + if (!m_file_spec.Exists()) + { + 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 = m_file_spec.GetModificationTime(); + } + } + } + } + + if (m_mod_time.IsValid()) + m_data_sp = m_file_spec.ReadFileContents (); +} + +SourceManager::File::~File() +{ +} + +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; +} + +bool +SourceManager::File::LineIsValid (uint32_t line) +{ + if (line == 0) + return false; + + if (CalculateLineOffsets (line)) + return line < m_offsets.size(); + return false; +} + +size_t +SourceManager::File::DisplaySourceLines (uint32_t line, uint32_t context_before, uint32_t context_after, Stream *s) +{ + // 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. + TimeValue curr_mod_time (m_file_spec.GetModificationTime()); + + if (curr_mod_time.IsValid() && m_mod_time != curr_mod_time) + { + m_mod_time = curr_mod_time; + m_data_sp = m_file_spec.ReadFileContents (); + m_offsets.clear(); + } + + // Sanity check m_data_sp before proceeding. + if (!m_data_sp) + return 0; + + 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); + size_t bytes_written = 0; + 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; + bytes_written = s->Write(cstr, count); + if (!is_newline_char(cstr[count-1])) + bytes_written += s->EOL(); + } + return bytes_written; + } + return 0; +} + +void +SourceManager::File::FindLinesMatchingRegex (RegularExpression& regex, uint32_t start_line, uint32_t end_line, std::vector &match_lines) +{ + TimeValue curr_mod_time (m_file_spec.GetModificationTime()); + if (m_mod_time != curr_mod_time) + { + m_mod_time = curr_mod_time; + m_data_sp = m_file_spec.ReadFileContents (); + m_offsets.clear(); + } + + 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.c_str())) + { + 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) + { + if (lhs.m_mod_time.IsValid()) + { + if (rhs.m_mod_time.IsValid()) + return lhs.m_mod_time == rhs.m_mod_time; + else + return false; + } + else if (rhs.m_mod_time.IsValid()) + return false; + else + return true; + } + else + return false; +} + +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() == NULL) + 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); + register const char *s; + for (s = start; s < end; ++s) + { + register char curr_ch = *s; + if (is_newline_char (curr_ch)) + { + if (s + 1 < end) + { + register 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() < 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" == NULL); + } + + } + else + { + // Calculate all line offsets up to "line" + assert("Not implemented yet" == NULL); + } + 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/source/Core/State.cpp b/source/Core/State.cpp new file mode 100644 index 00000000000..7d9ccda532a --- /dev/null +++ b/source/Core/State.cpp @@ -0,0 +1,115 @@ +//===-- State.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/State.h" +#include + +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"; + } + static char unknown_state_string[64]; + snprintf(unknown_state_string, sizeof (unknown_state_string), "StateType = %i", state); + return unknown_state_string; +} + +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/source/Core/Stream.cpp b/source/Core/Stream.cpp new file mode 100644 index 00000000000..49c15d63c36 --- /dev/null +++ b/source/Core/Stream.cpp @@ -0,0 +1,786 @@ +//===-- Stream.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Endian.h" +#include +#include +#include +#include + +#include + +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) +{ +} + +Stream::Stream () : + m_flags (0), + m_addr_size (4), + m_byte_order (lldb::endian::InlHostByteOrder()), + m_indent_level(0) +{ +} + +//------------------------------------------------------------------ +// 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) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + bool more = true; + while (more) + { + uint8_t byte = sval & 0x7fu; + sval >>= 7; + /* sign bit of byte is 2nd high order bit (0x40) */ + if ((sval == 0 && !(byte & 0x40)) || + (sval == -1 && (byte & 0x40)) ) + more = false; + else + // more bytes to come + byte |= 0x80u; + bytes_written += Write(&byte, 1); + } + } + else + { + bytes_written = Printf ("0x%" PRIi64, sval); + } + + return bytes_written; + +} + +//------------------------------------------------------------------ +// Put an ULEB128 "uval" out to the stream using the printf format +// in "format". +//------------------------------------------------------------------ +size_t +Stream::PutULEB128 (uint64_t uval) +{ + size_t bytes_written = 0; + if (m_flags.Test(eBinary)) + { + do + { + + uint8_t byte = uval & 0x7fu; + uval >>= 7; + if (uval != 0) + { + // more bytes to come + byte |= 0x80u; + } + bytes_written += Write(&byte, 1); + } while (uval != 0); + } + else + { + bytes_written = Printf ("0x%" PRIx64, uval); + } + return bytes_written; +} + +//------------------------------------------------------------------ +// Print a raw NULL terminated C string to the stream. +//------------------------------------------------------------------ +size_t +Stream::PutCString (const char *cstr) +{ + size_t cstr_len = strlen(cstr); + // when in binary mode, emit the NULL terminator + if (m_flags.Test(eBinary)) + ++cstr_len; + return Write (cstr, cstr_len); +} + +//------------------------------------------------------------------ +// 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 == NULL) + prefix = ""; + if (suffix == NULL) + 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, (uint64_t)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) +{ + char str[1024]; + va_list args_copy; + + va_copy (args_copy, args); + + size_t bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + // Include the NULL termination byte for binary output + if (m_flags.Test(eBinary)) + length += 1; + // The formatted string fit into our stack based buffer, so we can just + // append that to our packet + bytes_written = Write (str, length); + } + else + { + // Our stack buffer wasn't big enough to contain the entire formatted + // string, so lets let vasprintf create the string for us! + char *str_ptr = NULL; + length = ::vasprintf (&str_ptr, format, args_copy); + if (str_ptr) + { + // Include the NULL termination byte for binary output + if (m_flags.Test(eBinary)) + length += 1; + bytes_written = Write (str_ptr, length); + ::free (str_ptr); + } + } + va_end (args_copy); + return bytes_written; +} + +//------------------------------------------------------------------ +// 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 idenatation spaces. +//------------------------------------------------------------------ +size_t +Stream::Indent(const char *s) +{ + return Printf ("%*.*s%s", m_indent_level, m_indent_level, "", s ? s : ""); +} + +//------------------------------------------------------------------ +// 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 the pointer value out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (void *p) +{ + Printf ("0x%.*tx", (int)sizeof(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", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int16_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int16_t sval) +{ + Printf ("%i", (int)sval); + return *this; +} + +//------------------------------------------------------------------ +// Stream a int32_t "sval" out to this stream. +//------------------------------------------------------------------ +Stream& +Stream::operator<< (int32_t sval) +{ + Printf ("%i", (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; +} + +//------------------------------------------------------------------ +// Returns true if the verbose flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetVerbose() const +{ + return m_flags.Test(eVerbose); +} + +//------------------------------------------------------------------ +// Returns true if the debug flag bit is set in this stream. +//------------------------------------------------------------------ +bool +Stream::GetDebug() const +{ + return m_flags.Test(eDebug); +} + +//------------------------------------------------------------------ +// 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_list args_copy; + va_start (args, format); + va_copy (args, args_copy); // Copy this so we + + char str[1024]; + size_t bytes_written = 0; + // Try and format our string into a fixed buffer first and see if it fits + size_t length = ::vsnprintf (str, sizeof(str), format, args); + if (length < sizeof(str)) + { + // The formatted string fit into our stack based buffer, so we can just + // append that to our packet + for (size_t i=0; i> 4) & 0xf]; + nibble_chars[1] = g_hex_to_ascii_hex_char[(uvalue >> 0) & 0xf]; + bytes_written = Write (nibble_chars, sizeof(nibble_chars)); + } + return bytes_written; +} + +size_t +Stream::PutHex8 (uint8_t uvalue) +{ + return _PutHex8 (uvalue, m_flags.Test(eAddPrefix)); +} + +size_t +Stream::PutHex16 (uint16_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutHex32(uint32_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutHex64(uint64_t uvalue, ByteOrder byte_order) +{ + if (byte_order == eByteOrderInvalid) + byte_order = m_byte_order; + + bool add_prefix = m_flags.Test(eAddPrefix); + size_t bytes_written = 0; + if (byte_order == eByteOrderLittle) + { + for (size_t byte = 0; byte < sizeof(uvalue); ++byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + else + { + for (size_t byte = sizeof(uvalue)-1; byte < sizeof(uvalue); --byte, add_prefix = false) + bytes_written += _PutHex8 ((uint8_t)(uvalue >> (byte * 8)), add_prefix); + } + return bytes_written; +} + +size_t +Stream::PutMaxHex64 +( + uint64_t uvalue, + size_t byte_size, + lldb::ByteOrder byte_order +) +{ + switch (byte_size) + { + case 1: return PutHex8 ((uint8_t)uvalue); + case 2: return PutHex16 ((uint16_t)uvalue); + case 4: return PutHex32 ((uint32_t)uvalue); + case 8: return PutHex64 (uvalue); + } + return 0; +} + +size_t +Stream::PutPointer (void *ptr) +{ + return PutRawBytes (&ptr, sizeof(ptr), lldb::endian::InlHostByteOrder(), lldb::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), lldb::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), lldb::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), lldb::endian::InlHostByteOrder(), byte_order); +} + +size_t +Stream::PutRawBytes (const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) +{ + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (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) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (size_t i = src_len-1; i < src_len; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (!binary_was_set) + m_flags.Clear (eBinary); + + return bytes_written; +} + +size_t +Stream::PutBytesAsRawHex8 (const void *s, size_t src_len, ByteOrder src_byte_order, ByteOrder dst_byte_order) +{ + if (src_byte_order == eByteOrderInvalid) + src_byte_order = m_byte_order; + + if (dst_byte_order == eByteOrderInvalid) + dst_byte_order = m_byte_order; + + size_t bytes_written = 0; + const uint8_t *src = (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) + bytes_written += _PutHex8 (src[i], false); + } + else + { + for (size_t i = src_len-1; i < src_len; --i) + bytes_written += _PutHex8 (src[i], false); + } + if (binary_is_set) + m_flags.Set(eBinary); + + return bytes_written; +} + +size_t +Stream::PutCStringAsRawHex8 (const char *s) +{ + size_t bytes_written = 0; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + do + { + bytes_written += _PutHex8 (*s, false); + ++s; + } while (*s); + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + +void +Stream::UnitTest(Stream *s) +{ + s->PutHex8(0x12); + + s->PutChar(' '); + s->PutHex16(0x3456, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderBig); + s->PutChar(' '); + s->PutHex16(0x3456, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex32(0x789abcde, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderBig); + s->PutChar(' '); + s->PutHex32(0x789abcde, eByteOrderLittle); + + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, lldb::endian::InlHostByteOrder()); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderBig); + s->PutChar(' '); + s->PutHex64(0x1122334455667788ull, eByteOrderLittle); + + const char *hola = "Hello World!!!"; + s->PutChar(' '); + s->PutCString (hola); + + s->PutChar(' '); + s->Write (hola, 5); + + s->PutChar(' '); + s->PutCStringAsRawHex8 (hola); + + s->PutChar(' '); + s->PutCStringAsRawHex8 ("01234"); + + s->PutChar(' '); + s->Printf ("pid=%i", 12733); + + s->PutChar(' '); + s->PrintfAsRawHex8 ("pid=%i", 12733); + s->PutChar('\n'); +} + diff --git a/source/Core/StreamAsynchronousIO.cpp b/source/Core/StreamAsynchronousIO.cpp new file mode 100644 index 00000000000..b9e5cdfde72 --- /dev/null +++ b/source/Core/StreamAsynchronousIO.cpp @@ -0,0 +1,52 @@ +//===-- StreamBroadcast.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamAsynchronousIO.h" + +using namespace lldb; +using namespace lldb_private; + + +StreamAsynchronousIO::StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type) : + Stream (0, 4, eByteOrderBig), + m_broadcaster (broadcaster), + m_broadcast_event_type (broadcast_event_type), + m_accumulated_data () +{ +} + +StreamAsynchronousIO::~StreamAsynchronousIO () +{ +} + +void +StreamAsynchronousIO::Flush () +{ + if (m_accumulated_data.GetSize() > 0) + { + std::unique_ptr data_bytes_ap (new EventDataBytes); + // Let's swap the bytes to avoid LARGE string copies. + data_bytes_ap->SwapBytes (m_accumulated_data.GetString()); + EventSP new_event_sp (new Event (m_broadcast_event_type, data_bytes_ap.release())); + m_broadcaster.BroadcastEvent (new_event_sp); + m_accumulated_data.Clear(); + } +} + +size_t +StreamAsynchronousIO::Write (const void *s, size_t length) +{ + m_accumulated_data.Write (s, length); + return length; +} diff --git a/source/Core/StreamCallback.cpp b/source/Core/StreamCallback.cpp new file mode 100644 index 00000000000..d144b16755e --- /dev/null +++ b/source/Core/StreamCallback.cpp @@ -0,0 +1,64 @@ +//===-- StreamCallback.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/lldb-private.h" +#include "lldb/Core/Broadcaster.h" +#include "lldb/Core/Event.h" +#include "lldb/Core/StreamCallback.h" +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + + +StreamCallback::StreamCallback (lldb::LogOutputCallback callback, void *baton) : + Stream (0, 4, eByteOrderBig), + m_callback (callback), + m_baton (baton), + m_accumulated_data (), + m_collection_mutex () +{ +} + +StreamCallback::~StreamCallback () +{ +} + +StreamString & +StreamCallback::FindStreamForThread(lldb::tid_t cur_tid) +{ + Mutex::Locker locker(m_collection_mutex); + collection::iterator iter = m_accumulated_data.find (cur_tid); + if (iter == m_accumulated_data.end()) + { + std::pair ret; + ret = m_accumulated_data.insert(std::pair(cur_tid, StreamString())); + iter = ret.first; + } + return (*iter).second; +} + +void +StreamCallback::Flush () +{ + lldb::tid_t cur_tid = Host::GetCurrentThreadID(); + StreamString &out_stream = FindStreamForThread(cur_tid); + m_callback (out_stream.GetData(), m_baton); + out_stream.Clear(); +} + +size_t +StreamCallback::Write (const void *s, size_t length) +{ + lldb::tid_t cur_tid = Host::GetCurrentThreadID(); + FindStreamForThread(cur_tid).Write (s, length); + return length; +} diff --git a/source/Core/StreamFile.cpp b/source/Core/StreamFile.cpp new file mode 100644 index 00000000000..9a4eb796dbe --- /dev/null +++ b/source/Core/StreamFile.cpp @@ -0,0 +1,72 @@ +//===-- StreamFile.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamFile.h" + +// C Includes +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.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 (path, File::eOpenOptionWrite | File::eOpenOptionCanCreate, File::ePermissionsDefault) +{ +} + + +StreamFile::~StreamFile() +{ +} + +void +StreamFile::Flush () +{ + m_file.Flush(); +} + +size_t +StreamFile::Write (const void *s, size_t length) +{ + m_file.Write (s, length); + return length; +} diff --git a/source/Core/StreamString.cpp b/source/Core/StreamString.cpp new file mode 100644 index 00000000000..8d7d039fd65 --- /dev/null +++ b/source/Core/StreamString.cpp @@ -0,0 +1,100 @@ +//===-- StreamString.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StreamString.h" +#include + +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::Write (const void *s, size_t length) +{ + m_packet.append ((char *)s, length); + return length; +} + +void +StreamString::Clear() +{ + m_packet.clear(); +} + +bool +StreamString::Empty() const +{ + return GetSize() == 0; +} + +const char * +StreamString::GetData () const +{ + return m_packet.c_str(); +} + +size_t +StreamString::GetSize () const +{ + return m_packet.size(); +} + +std::string & +StreamString::GetString() +{ + return m_packet; +} + +const std::string & +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/source/Core/StringList.cpp b/source/Core/StringList.cpp new file mode 100644 index 00000000000..99497511678 --- /dev/null +++ b/source/Core/StringList.cpp @@ -0,0 +1,290 @@ +//===-- StringList.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/StringList.h" + +#include "lldb/Core/StreamString.h" +#include "lldb/Host/FileSpec.h" + +#include + +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 (const char *str, size_t str_len) +{ + if (str) + m_strings.push_back (std::string (str, str_len)); +} + +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)); +} + +bool +StringList::ReadFileLines (FileSpec &input_file) +{ + return input_file.ReadFileLines (m_strings); +} + +size_t +StringList::GetSize () const +{ + return m_strings.size(); +} + +const char * +StringList::GetStringAtIndex (size_t idx) const +{ + if (idx < m_strings.size()) + return m_strings[idx].c_str(); + return NULL; +} + +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) +{ + //arg_sstr_collection::iterator pos, end = m_args.end(); + size_t pos = 0; + size_t end = m_strings.size(); + + if (pos == end) + common_prefix.clear(); + else + common_prefix = m_strings[pos]; + + for (++pos; pos != end; ++pos) + { + size_t new_size = strlen (m_strings[pos].c_str()); + + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); + + // Then trim it at the first disparity: + + for (size_t i = 0; i < common_prefix.size(); i++) + { + if (m_strings[pos][i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } + } + + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } +} + +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::DeleteStringAtIndex (size_t idx) +{ + if (idx < m_strings.size()) + m_strings.erase (m_strings.begin() + idx); +} + +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) +{ + 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 std::string(strm.GetData()); +} + +StringList& +StringList::operator << (const char* str) +{ + AppendString(str); + return *this; +} + +StringList& +StringList::operator << (StringList strings) +{ + AppendList(strings); + return *this; +} + +size_t +StringList::AutoComplete (const char *s, StringList &matches, size_t &exact_idx) const +{ + matches.Clear(); + exact_idx = SIZE_MAX; + if (s && s[0]) + { + const size_t s_len = strlen (s); + const size_t num_strings = m_strings.size(); + + for (size_t i=0; i +#include +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/Mutex.h" + +#include + +using namespace lldb_private; + +#define TIMER_INDENT_AMOUNT 2 +static bool g_quiet = true; +uint32_t Timer::g_depth = 0; +uint32_t Timer::g_display_depth = 0; +FILE * Timer::g_file = NULL; +typedef std::vector TimerStack; +typedef std::map TimerCategoryMap; +static pthread_key_t g_key; + +static Mutex & +GetCategoryMutex() +{ + static Mutex g_category_mutex(Mutex::eMutexTypeNormal); + return g_category_mutex; +} + +static TimerCategoryMap & +GetCategoryMap() +{ + static TimerCategoryMap g_category_map; + return g_category_map; +} + + +static TimerStack * +GetTimerStackForCurrentThread () +{ + void *timer_stack = ::pthread_getspecific (g_key); + if (timer_stack == NULL) + { + ::pthread_setspecific (g_key, new TimerStack); + timer_stack = ::pthread_getspecific (g_key); + } + return (TimerStack *)timer_stack; +} + +void +ThreadSpecificCleanup (void *p) +{ + delete (TimerStack *)p; +} + +void +Timer::SetQuiet (bool value) +{ + g_quiet = value; +} + +void +Timer::Initialize () +{ + Timer::g_file = stdout; + ::pthread_key_create (&g_key, ThreadSpecificCleanup); + +} + +Timer::Timer (const char *category, const char *format, ...) : + m_category (category), + m_total_start (), + m_timer_start (), + m_total_ticks (0), + m_timer_ticks (0) +{ + if (g_depth++ < g_display_depth) + { + if (g_quiet == false) + { + // Indent + ::fprintf (g_file, "%*s", g_depth * TIMER_INDENT_AMOUNT, ""); + // Print formatted string + va_list args; + va_start (args, format); + ::vfprintf (g_file, format, args); + va_end (args); + + // Newline + ::fprintf (g_file, "\n"); + } + TimeValue start_time(TimeValue::Now()); + m_total_start = start_time; + m_timer_start = start_time; + TimerStack *stack = GetTimerStackForCurrentThread (); + if (stack) + { + if (stack->empty() == false) + stack->back()->ChildStarted (start_time); + stack->push_back(this); + } + } +} + + +Timer::~Timer() +{ + if (m_total_start.IsValid()) + { + TimeValue stop_time = TimeValue::Now(); + if (m_total_start.IsValid()) + { + m_total_ticks += (stop_time - m_total_start); + m_total_start.Clear(); + } + if (m_timer_start.IsValid()) + { + m_timer_ticks += (stop_time - m_timer_start); + m_timer_start.Clear(); + } + + TimerStack *stack = GetTimerStackForCurrentThread (); + if (stack) + { + assert (stack->back() == this); + stack->pop_back(); + if (stack->empty() == false) + stack->back()->ChildStopped(stop_time); + } + + const uint64_t total_nsec_uint = GetTotalElapsedNanoSeconds(); + const uint64_t timer_nsec_uint = GetTimerElapsedNanoSeconds(); + const double total_nsec = total_nsec_uint; + const double timer_nsec = timer_nsec_uint; + + if (g_quiet == false) + { + + ::fprintf (g_file, + "%*s%.9f sec (%.9f sec)\n", + (g_depth - 1) *TIMER_INDENT_AMOUNT, "", + total_nsec / 1000000000.0, + timer_nsec / 1000000000.0); + } + + // Keep total results for each category so we can dump results. + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + category_map[m_category] += timer_nsec_uint; + } + if (g_depth > 0) + --g_depth; +} + +uint64_t +Timer::GetTotalElapsedNanoSeconds() +{ + uint64_t total_ticks = m_total_ticks; + + // If we are currently running, we need to add the current + // elapsed time of the running timer... + if (m_total_start.IsValid()) + total_ticks += (TimeValue::Now() - m_total_start); + + return total_ticks; +} + +uint64_t +Timer::GetTimerElapsedNanoSeconds() +{ + uint64_t timer_ticks = m_timer_ticks; + + // If we are currently running, we need to add the current + // elapsed time of the running timer... + if (m_timer_start.IsValid()) + timer_ticks += (TimeValue::Now() - m_timer_start); + + return timer_ticks; +} + +void +Timer::ChildStarted (const TimeValue& start_time) +{ + if (m_timer_start.IsValid()) + { + m_timer_ticks += (start_time - m_timer_start); + m_timer_start.Clear(); + } +} + +void +Timer::ChildStopped (const TimeValue& stop_time) +{ + if (!m_timer_start.IsValid()) + m_timer_start = stop_time; +} + +void +Timer::SetDisplayDepth (uint32_t depth) +{ + g_display_depth = depth; +} + + +/* binary function predicate: + * - returns whether a person is less than another person + */ +static bool +CategoryMapIteratorSortCriterion (const TimerCategoryMap::const_iterator& lhs, const TimerCategoryMap::const_iterator& rhs) +{ + return lhs->second > rhs->second; +} + + +void +Timer::ResetCategoryTimes () +{ + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + category_map.clear(); +} + +void +Timer::DumpCategoryTimes (Stream *s) +{ + Mutex::Locker locker (GetCategoryMutex()); + TimerCategoryMap &category_map = GetCategoryMap(); + std::vector sorted_iterators; + TimerCategoryMap::const_iterator pos, end = category_map.end(); + for (pos = category_map.begin(); pos != end; ++pos) + { + sorted_iterators.push_back (pos); + } + std::sort (sorted_iterators.begin(), sorted_iterators.end(), CategoryMapIteratorSortCriterion); + + const size_t count = sorted_iterators.size(); + for (size_t i=0; isecond; + s->Printf("%.9f sec for %s\n", timer_nsec / 1000000000.0, sorted_iterators[i]->first); + } +} diff --git a/source/Core/UUID.cpp b/source/Core/UUID.cpp new file mode 100644 index 00000000000..c1b3eb13d65 --- /dev/null +++ b/source/Core/UUID.cpp @@ -0,0 +1,279 @@ +//===-- UUID.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/UUID.h" +// C Includes +#include +#include +#include + +// C++ Includes +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + +namespace lldb_private { + +UUID::UUID() : m_num_uuid_bytes(16) +{ + ::memset (m_uuid, 0, sizeof(m_uuid)); +} + +UUID::UUID(const UUID& rhs) +{ + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); +} + +UUID::UUID (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + SetBytes (uuid_bytes, num_uuid_bytes); +} + +const UUID& +UUID::operator=(const UUID& rhs) +{ + if (this != &rhs) + { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; + ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); + } + return *this; +} + +UUID::~UUID() +{ +} + +void +UUID::Clear() +{ + m_num_uuid_bytes = 16; + ::memset (m_uuid, 0, sizeof(m_uuid)); +} + +const void * +UUID::GetBytes() const +{ + return m_uuid; +} + +std::string +UUID::GetAsString (const char *separator) const +{ + std::string result; + char buf[256]; + if (!separator) + separator = "-"; + const uint8_t *u = (const uint8_t *)GetBytes(); + if (sizeof (buf) > (size_t)snprintf (buf, + sizeof (buf), + "%2.2X%2.2X%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%s%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", + u[0],u[1],u[2],u[3],separator, + u[4],u[5],separator, + u[6],u[7],separator, + u[8],u[9],separator, + u[10],u[11],u[12],u[13],u[14],u[15])) + { + result.append (buf); + if (m_num_uuid_bytes == 20) + { + if (sizeof (buf) > (size_t)snprintf (buf, sizeof (buf), "%s%2.2X%2.2X%2.2X%2.2X", separator,u[16],u[17],u[18],u[19])) + result.append (buf); + } + } + return result; +} + +void +UUID::Dump (Stream *s) const +{ + const uint8_t *u = (const uint8_t *)GetBytes(); + s->Printf ("%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", + u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8],u[9],u[10],u[11],u[12],u[13],u[14],u[15]); + if (m_num_uuid_bytes == 20) + { + s->Printf ("-%2.2X%2.2X%2.2X%2.2X", u[16],u[17],u[18],u[19]); + } +} + +bool +UUID::SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes) +{ + if (uuid_bytes) + { + switch (num_uuid_bytes) + { + case 20: + m_num_uuid_bytes = 20; + break; + case 16: + m_num_uuid_bytes = 16; + m_uuid[16] = m_uuid[17] = m_uuid[18] = m_uuid[19] = 0; + break; + default: + // Unsupported UUID byte size + m_num_uuid_bytes = 0; + break; + } + + if (m_num_uuid_bytes > 0) + { + ::memcpy (m_uuid, uuid_bytes, m_num_uuid_bytes); + return true; + } + } + ::memset (m_uuid, 0, sizeof(m_uuid)); + return false; +} + +size_t +UUID::GetByteSize() +{ + return m_num_uuid_bytes; +} + +bool +UUID::IsValid () const +{ + return m_uuid[0] || + m_uuid[1] || + m_uuid[2] || + m_uuid[3] || + m_uuid[4] || + m_uuid[5] || + m_uuid[6] || + m_uuid[7] || + m_uuid[8] || + m_uuid[9] || + m_uuid[10] || + m_uuid[11] || + m_uuid[12] || + m_uuid[13] || + m_uuid[14] || + m_uuid[15] || + m_uuid[16] || + m_uuid[17] || + m_uuid[18] || + m_uuid[19]; +} + +static inline int +xdigit_to_int (char ch) +{ + ch = tolower(ch); + if (ch >= 'a' && ch <= 'f') + return 10 + ch - 'a'; + return ch - '0'; +} + +size_t +UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes) +{ + size_t uuid_byte_idx = 0; + if (p) + { + while (*p) + { + 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[uuid_byte_idx] = (hi_nibble << 4) + lo_nibble; + + // Skip both hex digits + p += 2; + + // Increment the byte that we are decoding within the UUID value + // and break out if we are done + if (++uuid_byte_idx == num_uuid_bytes) + break; + } + else if (*p == '-') + { + // Skip dashes + p++; + } + else + { + // UUID values can only consist of hex characters and '-' chars + break; + } + } + } + if (end) + *end = p; + // Clear trailing bytes to 0. + for (uint32_t i = uuid_byte_idx; i < sizeof(ValueType); i++) + uuid_bytes[i] = 0; + return uuid_byte_idx; +} +size_t +UUID::SetFromCString (const char *cstr, uint32_t num_uuid_bytes) +{ + if (cstr == NULL) + return 0; + + const char *p = cstr; + + // Skip leading whitespace characters + while (isspace(*p)) + ++p; + + const size_t uuid_byte_idx = UUID::DecodeUUIDBytesFromCString (p, m_uuid, &p, num_uuid_bytes); + + // If we successfully decoded a UUID, return the amount of characters that + // were consumed + if (uuid_byte_idx == num_uuid_bytes) + return p - cstr; + + // Else return zero to indicate we were not able to parse a UUID value + return 0; +} + +} + +bool +lldb_private::operator == (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) == 0; +} + +bool +lldb_private::operator != (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) != 0; +} + +bool +lldb_private::operator < (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) < 0; +} + +bool +lldb_private::operator <= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) <= 0; +} + +bool +lldb_private::operator > (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) > 0; +} + +bool +lldb_private::operator >= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) +{ + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) >= 0; +} diff --git a/source/Core/UserID.cpp b/source/Core/UserID.cpp new file mode 100644 index 00000000000..f3d6e5b2218 --- /dev/null +++ b/source/Core/UserID.cpp @@ -0,0 +1,23 @@ +//===-- UserID.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/UserID.h" +#include "lldb/Core/Stream.h" + +#include + +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/source/Core/UserSettingsController.cpp b/source/Core/UserSettingsController.cpp new file mode 100644 index 00000000000..63a5dd9ed51 --- /dev/null +++ b/source/Core/UserSettingsController.cpp @@ -0,0 +1,111 @@ +//====-- UserSettingsController.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include +#include + +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueString.h" + +using namespace lldb; +using namespace lldb_private; + + +lldb::OptionValueSP +Properties::GetPropertyValue (const ExecutionContext *exe_ctx, + const char *path, + bool will_modify, + Error &error) const +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->GetSubValue(exe_ctx, path, will_modify, error); + return lldb::OptionValueSP(); +} + +Error +Properties::SetPropertyValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *path, + const char *value) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->SetSubValue(exe_ctx, op, path, value); + Error 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); +} + + + +Error +Properties::DumpPropertyValue (const ExecutionContext *exe_ctx, Stream &strm, const char *property_path, uint32_t dump_mask) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + { + return properties_sp->DumpPropertyValue (exe_ctx, + strm, + property_path, + dump_mask); + } + Error error; + error.SetErrorString("empty property list"); + return error; +} + +size_t +Properties::Apropos (const char *keyword, std::vector &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, + const ConstString &name) +{ + OptionValuePropertiesSP properties_sp (GetValueProperties ()); + if (properties_sp) + return properties_sp->GetSubProperty (exe_ctx, name); + return lldb::OptionValuePropertiesSP(); +} + diff --git a/source/Core/VMRange.cpp b/source/Core/VMRange.cpp new file mode 100644 index 00000000000..902489e1ff3 --- /dev/null +++ b/source/Core/VMRange.cpp @@ -0,0 +1,112 @@ +//===-- VMRange.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-private.h" + +#include "lldb/Core/Stream.h" +#include "lldb/Core/VMRange.h" +#include + +using namespace lldb; +using namespace lldb_private; + +bool +VMRange::ContainsValue(const VMRange::collection& coll, lldb::addr_t value) +{ + ValueInRangeUnaryPredicate in_range_predicate(value); + VMRange::const_iterator pos; + VMRange::const_iterator end = coll.end(); + pos = std::find_if( coll.begin(), end, in_range_predicate ); + if (pos != end) + return true; + return false; +} + +bool +VMRange::ContainsRange(const VMRange::collection& coll, const VMRange& range) +{ + RangeInRangeUnaryPredicate in_range_predicate(range); + VMRange::const_iterator pos; + VMRange::const_iterator end = coll.end(); + pos = std::find_if( coll.begin(), end, in_range_predicate ); + if (pos != end) + return true; + return false; +} + +size_t +VMRange::FindRangeIndexThatContainsValue (const VMRange::collection& coll, lldb::addr_t value) +{ + ValueInRangeUnaryPredicate in_range_predicate(value); + VMRange::const_iterator begin = coll.begin(); + VMRange::const_iterator end = coll.end(); + VMRange::const_iterator pos = std::find_if (begin, end, in_range_predicate); + if (pos != end) + return std::distance (begin, pos); + return UINT32_MAX; +} + +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.GetBaseAddress() != rhs.GetBaseAddress() || lhs.GetEndAddress() != rhs.GetEndAddress(); +} + +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) +{ + 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) +{ + 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) +{ + if (lhs.GetBaseAddress() > rhs.GetBaseAddress()) + return true; + else if (lhs.GetBaseAddress() < rhs.GetBaseAddress()) + return false; + return lhs.GetEndAddress() >= rhs.GetEndAddress(); +} + diff --git a/source/Core/Value.cpp b/source/Core/Value.cpp new file mode 100644 index 00000000000..3fe75d3d4ce --- /dev/null +++ b/source/Core/Value.cpp @@ -0,0 +1,761 @@ +//===-- Value.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Value.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/State.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.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/Target.h" + +using namespace lldb; +using namespace lldb_private; + +Value::Value() : + m_value (), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeScalar), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ +} + +Value::Value(const Scalar& scalar) : + m_value (scalar), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeScalar), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ +} + + +Value::Value(const uint8_t *bytes, int len) : + m_value (), + m_vector (), + m_clang_type (), + m_context (NULL), + m_value_type (eValueTypeHostAddress), + m_context_type (eContextTypeInvalid), + m_data_buffer () +{ + m_data_buffer.CopyData(bytes, len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +Value::Value(const Value &v) : + m_value (v.m_value), + m_vector (v.m_vector), + m_clang_type (v.m_clang_type), + m_context (v.m_context), + m_value_type (v.m_value_type), + m_context_type (v.m_context_type), + m_data_buffer () +{ + if ((uintptr_t)v.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (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_clang_type = rhs.m_clang_type; + m_context = rhs.m_context; + m_value_type = rhs.m_value_type; + m_context_type = rhs.m_context_type; + if ((uintptr_t)rhs.m_value.ULongLong(LLDB_INVALID_ADDRESS) == (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::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 (m_context); + return NULL; +} + +Type * +Value::GetType() +{ + if (m_context_type == eContextTypeLLDBType) + return static_cast (m_context); + return NULL; +} + +void +Value::ResizeData(size_t len) +{ + m_value_type = eValueTypeHostAddress; + m_data_buffer.SetByteSize(len); + m_value = (uintptr_t)m_data_buffer.GetBytes(); +} + +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 (Error *error_ptr) +{ + uint64_t byte_size = 0; + + switch (m_context_type) + { + case eContextTypeRegisterInfo: // RegisterInfo * + if (GetRegisterInfo()) + byte_size = GetRegisterInfo()->byte_size; + break; + + case eContextTypeInvalid: + case eContextTypeLLDBType: // Type * + case eContextTypeVariable: // Variable * + { + const ClangASTType &ast_type = GetClangType(); + if (ast_type.IsValid()) + byte_size = ast_type.GetByteSize(); + } + break; + } + + if (error_ptr) + { + if (byte_size == 0) + { + if (error_ptr->Success()) + error_ptr->SetErrorString("Unable to determine byte size."); + } + else + { + error_ptr->Clear(); + } + } + return byte_size; +} + +const ClangASTType & +Value::GetClangType () +{ + if (!m_clang_type.IsValid()) + { + switch (m_context_type) + { + case eContextTypeInvalid: + break; + + case eContextTypeRegisterInfo: + break; // TODO: Eventually convert into a clang type? + + case eContextTypeLLDBType: + { + Type *lldb_type = GetType(); + if (lldb_type) + m_clang_type = lldb_type->GetClangForwardType(); + } + break; + + case eContextTypeVariable: + { + Variable *variable = GetVariable(); + if (variable) + { + Type *variable_type = variable->GetType(); + if (variable_type) + m_clang_type = variable_type->GetClangForwardType(); + } + } + break; + } + } + + return m_clang_type; +} + +void +Value::SetClangType (const ClangASTType &clang_type) +{ + m_clang_type = clang_type; +} + +lldb::Format +Value::GetValueDefaultFormat () +{ + switch (m_context_type) + { + case eContextTypeRegisterInfo: + if (GetRegisterInfo()) + return GetRegisterInfo()->format; + break; + + case eContextTypeInvalid: + case eContextTypeLLDBType: + case eContextTypeVariable: + { + const ClangASTType &ast_type = GetClangType(); + 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; + +} + +Error +Value::GetValueAsData (ExecutionContext *exe_ctx, + DataExtractor &data, + uint32_t data_offset, + Module *module) +{ + data.Clear(); + + Error error; + lldb::addr_t address = LLDB_INVALID_ADDRESS; + AddressType address_type = eAddressTypeFile; + Address file_so_addr; + const ClangASTType &ast_type = GetClangType(); + 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 (lldb::endian::InlHostByteOrder()); + if (ast_type.IsValid()) + data.SetAddressByteSize (ast_type.GetPointerByteSize()); + else + data.SetAddressByteSize(sizeof(void *)); + if (m_value.GetData (data)) + return error; // Success; + error.SetErrorStringWithFormat("extracting data from value failed"); + break; + + case eValueTypeLoadAddress: + if (exe_ctx == NULL) + { + error.SetErrorString ("can't read load address (no execution context)"); + } + else + { + Process *process = exe_ctx->GetProcessPtr(); + if (process == NULL || !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 +// { +// ModuleSP exe_module_sp (target->GetExecutableModule()); +// if (exe_module_sp) +// { +// address = m_value.ULongLong(LLDB_INVALID_ADDRESS); +// if (address != LLDB_INVALID_ADDRESS) +// { +// if (exe_module_sp->ResolveFileAddress(address, file_so_addr)) +// { +// data.SetByteOrder(target->GetArchitecture().GetByteOrder()); +// data.SetAddressByteSize(target->GetArchitecture().GetAddressByteSize()); +// address_type = eAddressTypeFile; +// } +// 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 == NULL) + { + error.SetErrorString ("can't read file address (no execution context)"); + } + else if (exe_ctx->GetTargetPtr() == NULL) + { + 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 == NULL) + { + // 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(lldb::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); + + // Bail if we encountered any errors getting the byte size + if (error.Fail()) + 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)) + { + DataBufferSP data_sp(new DataBufferHeap (data_offset + byte_size, '\0')); + data.SetData(data_sp); + } + + uint8_t* dst = const_cast(data.PeekData (data_offset, byte_size)); + if (dst != NULL) + { + if (address_type == eAddressTypeHost) + { + // The address is an address in this process, so just copy it + memcpy (dst, (uint8_t*)NULL + 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 ClangASTType &clang_type = GetClangType(); + if (clang_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); + Error error (GetValueAsData (exe_ctx, data, 0, NULL)); + if (error.Success()) + { + Scalar scalar; + if (clang_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 (m_context); + return NULL; +} + +void +Value::Clear() +{ + m_value.Clear(); + m_vector.Clear(); + m_clang_type.Clear(); + m_value_type = eValueTypeScalar; + m_context = NULL; + 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 "???"; +} + +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 NULL; +} + +void +ValueList::Clear () +{ + m_values.clear(); +} + diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp new file mode 100644 index 00000000000..a30cc1306c6 --- /dev/null +++ b/source/Core/ValueObject.cpp @@ -0,0 +1,4199 @@ +//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObject.h" + +// C Includes +#include + +// C++ Includes +// Other libraries and framework includes +#include "llvm/Support/raw_ostream.h" +#include "clang/AST/Type.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectCast.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +#include "lldb/Host/Endian.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/LanguageRuntime.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +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 (NULL), + 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_manager(parent.GetManager()), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + 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_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(eAddressTypeInvalid), + 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_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 (NULL), + m_root (NULL), + 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_manager(), + m_children (), + m_synthetic_children (), + m_dynamic_value (NULL), + m_synthetic_value(NULL), + m_deref_valobj(NULL), + 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_user_id_of_forced_summary(), + m_address_type_of_ptr_or_ref_children(child_ptr_or_ref_addr_type), + 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_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 were asked to update your formatters, but did not get a chance to do it + // clear your own values (this serves the purpose of faking a stop-id for frozen + // objects (which are regarded as constant, but could have changes behind their backs + // because of the frozen-pointer depth limit) + // TODO: decouple summary from value and then remove this code and only force-clear the summary + if (update_format && !did_change_formats) + ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + return m_error.Success(); + } + + bool first_update = m_update_point.IsFirstEvaluation(); + + if (m_update_point.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 success = UpdateValue (); + + SetValueIsValid (success); + + if (first_update) + SetValueDidChange (false); + else if (!m_value_did_change && success == false) + { + // 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 + { + m_error.SetErrorString("out of scope"); + } + } + return m_error.Success(); +} + +bool +ValueObject::UpdateFormatsIfNeeded() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + if (log) + log->Printf("[%s %p] checking for FormatManager revisions. ValueObject rev: %d - Global rev: %d", + GetName().GetCString(), + this, + m_last_format_mgr_revision, + DataVisualization::GetCurrentRevision()); + + bool any_change = false; + + if ( (m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) + { + SetValueFormat(DataVisualization::ValueFormats::GetFormat (*this, eNoDynamicValues)); + SetSummaryFormat(DataVisualization::GetSummaryFormat (*this, GetDynamicValueType())); +#ifndef LLDB_DISABLE_PYTHON + SetSyntheticChildren(DataVisualization::GetSyntheticChildren (*this, GetDynamicValueType())); +#endif + + m_last_format_mgr_revision = DataVisualization::GetCurrentRevision(); + + any_change = true; + } + + 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_did_calculate_complete_objc_class_type = false; + m_last_format_mgr_revision = 0; + m_override_type = ClangASTType(); + SetValueFormat(lldb::TypeFormatImplSP()); + SetSummaryFormat(lldb::TypeSummaryImplSP()); + SetSyntheticChildren(lldb::SyntheticChildrenSP()); +} + +ClangASTType +ValueObject::MaybeCalculateCompleteType () +{ + ClangASTType clang_type(GetClangTypeImpl()); + + if (m_did_calculate_complete_objc_class_type) + { + if (m_override_type.IsValid()) + return m_override_type; + else + return clang_type; + } + + ClangASTType class_type; + bool is_pointer_type = false; + + if (clang_type.IsObjCObjectPointerType(&class_type)) + { + is_pointer_type = true; + } + else if (clang_type.IsObjCObjectOrInterfaceType()) + { + class_type = clang_type; + } + else + { + return clang_type; + } + + m_did_calculate_complete_objc_class_type = true; + + if (class_type) + { + ConstString class_name (class_type.GetConstTypeName()); + + if (class_name) + { + ProcessSP process_sp(GetUpdatePoint().GetExecutionContextRef().GetProcessSP()); + + if (process_sp) + { + ObjCLanguageRuntime *objc_language_runtime(process_sp->GetObjCLanguageRuntime()); + + if (objc_language_runtime) + { + TypeSP complete_objc_class_type_sp = objc_language_runtime->LookupInCompleteClassCache(class_name); + + if (complete_objc_class_type_sp) + { + ClangASTType complete_class(complete_objc_class_type_sp->GetClangFullType()); + + if (complete_class.GetCompleteType()) + { + if (is_pointer_type) + { + m_override_type = complete_class.GetPointerType(); + } + else + { + m_override_type = complete_class; + } + + if (m_override_type.IsValid()) + return m_override_type; + } + } + } + } + } + } + return clang_type; +} + +ClangASTType +ValueObject::GetClangType () +{ + return MaybeCalculateCompleteType(); +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + UpdateValueIfNeeded(false); + return m_data; +} + +const Error & +ValueObject::GetError() +{ + UpdateValueIfNeeded(false); + return m_error; +} + +const 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.swap(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::GetValueIsValid () const +{ + return m_value_is_valid; +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + m_value_is_valid = b; +} + +bool +ValueObject::GetValueDidChange () +{ + GetValueAsCString (); + 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 != NULL) + return child->GetSP(); + } + return child_sp; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list& 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; +} + +ValueObjectSP +ValueObject::GetChildAtIndexPath (const std::initializer_list< std::pair >& idxs, + size_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair 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::GetChildAtIndexPath (const std::vector &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 (const std::vector< std::pair > &idxs, + size_t* index_of_error) +{ + if (idxs.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair idx : idxs) + { + root = root->GetChildAtIndex(idx.first, idx.second); + if (!root) + { + if (index_of_error) + *index_of_error = idx.first; + return root; + } + } + return root; +} + +size_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return GetClangType().GetIndexOfChildWithName (name.GetCString(), omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const 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 child_indexes; + bool omit_empty_base_classes = true; + const size_t num_child_indexes = GetClangType().GetIndexOfChildMemberWithName (name.GetCString(), + omit_empty_base_classes, + child_indexes); + if (num_child_indexes > 0) + { + std::vector::const_iterator pos = child_indexes.begin (); + std::vector::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 () +{ + UpdateValueIfNeeded(); + 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 & (ClangASTType::eTypeHasChildren | + ClangASTType::eTypeIsPointer | + ClangASTType::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 (const ConstString &name) +{ + m_name = name; +} + +ValueObject * +ValueObject::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObject *valobj = NULL; + + 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; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = GetClangType().GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 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); + if (child_clang_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_clang_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); + //if (valobj) + // valobj->SetAddressTypeOfChildren(eAddressTypeInvalid); + } + + return valobj; +} + +bool +ValueObject::GetSummaryAsCString (TypeSummaryImpl* summary_ptr, + std::string& destination) +{ + 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; + + // 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)) + { + if (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); + } + else + { + ClangASTType clang_type = GetClangType(); + + // Do some default printout for function pointers + if (clang_type) + { + if (clang_type.IsFunctionPointerType ()) + { + StreamString sstr; + AddressType func_ptr_address_type = eAddressTypeInvalid; + addr_t func_ptr_address = 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: + break; + + case eAddressTypeLoad: + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + Address so_addr; + Target *target = exe_ctx.GetTargetPtr(); + if (target && target->GetSectionLoadList().IsEmpty() == false) + { + if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr)) + { + so_addr.Dump (&sstr, + exe_ctx.GetBestExecutionContextScope(), + Address::DumpStyleResolvedDescription, + Address::DumpStyleSectionNameOffset); + } + } + } + break; + + case eAddressTypeHost: + break; + } + } + if (sstr.GetSize() > 0) + { + destination.assign (1, '('); + destination.append (sstr.GetData(), sstr.GetSize()); + destination.append (1, ')'); + } + } + } + } + } + m_is_getting_summary = false; + return !destination.empty(); +} + +const char * +ValueObject::GetSummaryAsCString () +{ + if (UpdateValueIfNeeded(true) && m_summary_str.empty()) + { + GetSummaryAsCString(GetSummaryFormat().get(), + m_summary_str); + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + +bool +ValueObject::IsCStringContainer(bool check_pointer) +{ + ClangASTType pointee_or_element_clang_type; + const Flags type_flags (GetTypeInfo (&pointee_or_element_clang_type)); + bool is_char_arr_ptr (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + pointee_or_element_clang_type.IsCharType ()); + if (!is_char_arr_ptr) + return false; + if (!check_pointer) + return true; + if (type_flags.Test(ClangASTType::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) +{ + ClangASTType pointee_or_element_clang_type; + const uint32_t type_info = GetTypeInfo (&pointee_or_element_clang_type); + const bool is_pointer_type = type_info & ClangASTType::eTypeIsPointer; + const bool is_array_type = type_info & ClangASTType::eTypeIsArray; + if (!(is_pointer_type || is_array_type)) + return 0; + + if (item_count == 0) + return 0; + + const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(); + 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) + { + Error error; + ValueObjectSP pointee_sp = Dereference(error); + if (error.Fail() || pointee_sp.get() == NULL) + return 0; + return pointee_sp->GetDataExtractor().Copy(data); + } + else + { + ValueObjectSP child_sp = GetChildAtIndex(0, true); + if (child_sp.get() == NULL) + return 0; + return child_sp->GetDataExtractor().Copy(data); + } + return true; + } + else /* (items > 1) */ + { + Error error; + lldb_private::DataBufferHeap* heap_buf_ptr = NULL; + 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()) + { + data.SetData(data_sp); + return bytes_read; + } + } + } + break; + case eAddressTypeHost: + { + const uint64_t max_bytes = GetClangType().GetByteSize(); + if (max_bytes > offset) + { + size_t bytes_read = std::min(max_bytes - offset, bytes); + 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) +{ + UpdateValueIfNeeded(false); + ExecutionContext exe_ctx (GetExecutionContextRef()); + Error error = m_value.GetValueAsData(&exe_ctx, data, 0, GetModule().get()); + if (error.Fail()) + { + if (m_data.GetByteSize()) + { + data = m_data; + return data.GetByteSize(); + } + else + { + return 0; + } + } + data.SetAddressByteSize(m_data.GetAddressByteSize()); + data.SetByteOrder(m_data.GetByteOrder()); + return data.GetByteSize(); +} + +bool +ValueObject::SetData (DataExtractor &data, Error &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 = GetClangType().GetEncoding(count); + + const size_t byte_size = GetByteSize(); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeScalar: + { + Error 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(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; +} + +// will compute strlen(str), but without consuming more than +// maxlen bytes out of str (this serves the purpose of reading +// chunks of a string without having to worry about +// missing NULL terminators in the chunk) +// of course, if strlen(str) > maxlen, the function will return +// maxlen_value (which should be != maxlen, because that allows you +// to know whether strlen(str) == maxlen or strlen(str) > maxlen) +static uint32_t +strlen_or_inf (const char* str, + uint32_t maxlen, + uint32_t maxlen_value) +{ + uint32_t len = 0; + if (str) + { + while(*str) + { + len++;str++; + if (len >= maxlen) + return maxlen_value; + } + } + return len; +} + +size_t +ValueObject::ReadPointedString (Stream& s, + Error& error, + uint32_t max_length, + bool honor_array, + Format item_format) +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target* target = exe_ctx.GetTargetPtr(); + + if (!target) + { + s << ""; + error.SetErrorString("no target to read from"); + return 0; + } + + if (max_length == 0) + max_length = target->GetMaximumSizeOfStringSummary(); + + size_t bytes_read = 0; + size_t total_bytes_read = 0; + + ClangASTType clang_type = GetClangType(); + ClangASTType elem_or_pointee_clang_type; + const Flags type_flags (GetTypeInfo (&elem_or_pointee_clang_type)); + if (type_flags.AnySet (ClangASTType::eTypeIsArray | ClangASTType::eTypeIsPointer) && + elem_or_pointee_clang_type.IsCharType ()) + { + addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + + size_t cstr_len = 0; + bool capped_data = false; + if (type_flags.Test (ClangASTType::eTypeIsArray)) + { + // We have an array + uint64_t array_size = 0; + if (clang_type.IsArrayType(NULL, &array_size, NULL)) + { + 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) + { + s << ""; + error.SetErrorString("invalid address"); + return 0; + } + + 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; + s << '"'; + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 1, // Size of item (1 byte for a char!) + bytes_read, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + if (capped_data) + s << "..."; + s << '"'; + } + } + 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 = strlen_or_inf (cstr, k_max_buf_size, k_max_buf_size+1); + if (len > k_max_buf_size) + len = k_max_buf_size; + if (cstr && cstr_len_displayed < 0) + s << '"'; + + 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; + + data.Dump (&s, + 0, // Start offset in "data" + item_format, + 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 + + 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) + { + s << '"'; + if (capped_cstr) + s << "..."; + } + } + } + else + { + error.SetErrorString("not a string object"); + s << ""; + } + return total_bytes_read; +} + +const char * +ValueObject::GetObjectDescription () +{ + + if (!UpdateValueIfNeeded (true)) + return NULL; + + if (!m_object_desc_str.empty()) + return m_object_desc_str.c_str(); + + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + return NULL; + + StreamString s; + + LanguageType language = GetObjectRuntimeLanguage(); + LanguageRuntime *runtime = process->GetLanguageRuntime(language); + + if (runtime == NULL) + { + // Aw, hell, if the things a pointer, or even just an integer, let's try ObjC anyway... + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + bool is_signed; + if (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType ()) + { + runtime = process->GetLanguageRuntime(eLanguageTypeObjC); + } + } + } + + if (runtime && runtime->GetObjectDescription(s, *this)) + { + m_object_desc_str.append (s.GetData()); + } + + if (m_object_desc_str.empty()) + return NULL; + else + return m_object_desc_str.c_str(); +} + +bool +ValueObject::GetValueAsCString (lldb::Format format, + std::string& destination) +{ + if (GetClangType().IsAggregateType () == false && UpdateValueIfNeeded(false)) + { + const Value::ContextType context_type = m_value.GetContextType(); + + if (context_type == Value::eContextTypeRegisterInfo) + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + + StreamString reg_sstr; + m_data.Dump (®_sstr, + 0, + format, + reg_info->byte_size, + 1, + UINT32_MAX, + LLDB_INVALID_ADDRESS, + 0, + 0, + exe_ctx.GetBestExecutionContextScope()); + destination.swap(reg_sstr.GetString()); + } + } + else + { + ClangASTType clang_type = GetClangType (); + if (clang_type) + { + // put custom bytes to display in this DataExtractor to override the default value logic + lldb_private::DataExtractor special_format_data; + if (format == eFormatCString) + { + Flags type_flags(clang_type.GetTypeInfo(NULL)); + if (type_flags.Test(ClangASTType::eTypeIsPointer) && !type_flags.Test(ClangASTType::eTypeIsObjC)) + { + // if we are dumping a pointer as a c-string, get the pointee data as a string + TargetSP target_sp(GetTargetSP()); + if (target_sp) + { + size_t max_len = target_sp->GetMaximumSizeOfStringSummary(); + Error error; + DataBufferSP buffer_sp(new DataBufferHeap(max_len+1,0)); + Address address(GetPointerValue()); + if (target_sp->ReadCStringFromMemory(address, (char*)buffer_sp->GetBytes(), max_len, error) && error.Success()) + special_format_data.SetData(buffer_sp); + } + } + } + + StreamString sstr; + ExecutionContext exe_ctx (GetExecutionContextRef()); + clang_type.DumpTypeValue (&sstr, // The stream to use for display + format, // Format to display this type with + special_format_data.GetByteSize() ? + special_format_data: m_data, // Data to extract from + 0, // Byte offset into "m_data" + GetByteSize(), // Byte size of item in "m_data" + GetBitfieldBitSize(), // Bitfield bit size + GetBitfieldBitOffset(), // Bitfield bit offset + exe_ctx.GetBestExecutionContextScope()); + // Don't set the m_error to anything here otherwise + // we won't be able to re-format as anything else. The + // code for ClangASTType::DumpTypeValue() should always + // return something, even if that something contains + // an error messsage. "m_error" is used to detect errors + // when reading the valid object, not for formatting errors. + if (sstr.GetString().empty()) + destination.clear(); + else + destination.swap(sstr.GetString()); + } + } + return !destination.empty(); + } + else + return false; +} + +const char * +ValueObject::GetValueAsCString () +{ + if (UpdateValueIfNeeded(true)) + { + lldb::Format my_format = GetFormat(); + if (my_format == lldb::eFormatDefault) + { + if (m_type_format_sp) + my_format = m_type_format_sp->GetFormat(); + 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 = GetClangType().GetFormat(); + } + } + } + } + if (my_format != m_last_format || m_value_str.empty()) + { + m_last_format = my_format; + if (GetValueAsCString(my_format, 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 NULL; + 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 (!GetClangType().IsAggregateType()) + { + Scalar scalar; + if (ResolveValue (scalar)) + { + if (success) + *success = true; + return scalar.ULongLong(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(ClangASTType::eTypeIsArray | ClangASTType::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(ClangASTType::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) +{ + + Flags flags(GetTypeInfo()); + + bool allow_special = ((special & ePrintableRepresentationSpecialCasesAllow) == ePrintableRepresentationSpecialCasesAllow); + bool only_special = ((special & ePrintableRepresentationSpecialCasesOnly) == ePrintableRepresentationSpecialCasesOnly); + + if (allow_special) + { + if (flags.AnySet(ClangASTType::eTypeIsArray | ClangASTType::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 + { + Error error; + ReadPointedString(s, + error, + 0, + (custom_format == eFormatVectorOfChar) || + (custom_format == eFormatCharArray)); + 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(ClangASTType::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 << ""; + 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 << ""; + 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; + + { + const char *cstr = NULL; + + // 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: + cstr = GetValueAsCString(); + break; + + case eValueObjectRepresentationStyleSummary: + cstr = GetSummaryAsCString(); + break; + + case eValueObjectRepresentationStyleLanguageSpecific: + cstr = GetObjectDescription(); + break; + + case eValueObjectRepresentationStyleLocation: + cstr = GetLocationAsCString(); + break; + + case eValueObjectRepresentationStyleChildrenCount: + strm.Printf("%zu", GetNumChildren()); + cstr = strm.GetString().c_str(); + break; + + case eValueObjectRepresentationStyleType: + cstr = GetTypeName().AsCString(); + break; + + case eValueObjectRepresentationStyleName: + cstr = GetName().AsCString(); + break; + + case eValueObjectRepresentationStyleExpressionPath: + GetExpressionPath(strm, false); + cstr = strm.GetString().c_str(); + break; + } + + if (!cstr) + { + if (val_obj_display == eValueObjectRepresentationStyleValue) + cstr = GetSummaryAsCString(); + else if (val_obj_display == eValueObjectRepresentationStyleSummary) + { + if (GetClangType().IsAggregateType()) + { + strm.Printf("%s @ %s", GetTypeName().AsCString(), GetLocationAsCString()); + cstr = strm.GetString().c_str(); + } + else + cstr = GetValueAsCString(); + } + } + + if (cstr) + s.PutCString(cstr); + else + { + if (m_error.Fail()) + s.Printf("<%s>", m_error.AsCString()); + else if (val_obj_display == eValueObjectRepresentationStyleSummary) + s.PutCString(""); + else if (val_obj_display == eValueObjectRepresentationStyleValue) + s.PutCString(""); + else if (val_obj_display == eValueObjectRepresentationStyleLanguageSpecific) + s.PutCString(""); // edit this if we have other runtimes that support a description + else + s.PutCString(""); + } + + // 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) +{ + 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: + case Value::eValueTypeHostAddress: + { + if(address_type) + *address_type = m_value.GetValueAddressType (); + return m_value.GetScalar().ULongLong(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, Error& 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 = GetClangType().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 <= Scalar::GetMaxByteSize()) + { + // 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(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 GetClangType().GetConstTypeName(); +} + +ConstString +ValueObject::GetQualifiedTypeName() +{ + return GetClangType().GetConstQualifiedTypeName(); +} + + +LanguageType +ValueObject::GetObjectRuntimeLanguage () +{ + return GetClangType().GetMinimumLanguage (); +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObject *valobj) +{ + m_synthetic_children[key] = valobj; +} + +ValueObjectSP +ValueObject::GetSyntheticChild (const ConstString &key) const +{ + ValueObjectSP synthetic_child_sp; + std::map::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 (ClangASTType *pointee_or_element_clang_type) +{ + return GetClangType().GetTypeInfo (pointee_or_element_clang_type); +} + +bool +ValueObject::IsPointerType () +{ + return GetClangType().IsPointerType(); +} + +bool +ValueObject::IsArrayType () +{ + return GetClangType().IsArrayType (NULL, NULL, NULL); +} + +bool +ValueObject::IsScalarType () +{ + return GetClangType().IsScalarType (); +} + +bool +ValueObject::IsIntegerType (bool &is_signed) +{ + return GetClangType().IsIntegerType (is_signed); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return GetClangType().IsPointerOrReferenceType (); +} + +bool +ValueObject::IsPossibleDynamicType () +{ + ExecutionContext exe_ctx (GetExecutionContextRef()); + Process *process = exe_ctx.GetProcessPtr(); + if (process) + return process->IsPossibleDynamicValue(*this); + else + return GetClangType().IsPossibleDynamicType (NULL, true, true); +} + +bool +ValueObject::IsObjCNil () +{ + const uint32_t mask = ClangASTType::eTypeIsObjC | ClangASTType::eTypeIsPointer; + bool isObjCpointer = (((GetClangType().GetTypeInfo(NULL)) & mask) == mask); + if (!isObjCpointer) + return false; + bool canReadValue = true; + bool isZero = GetValueAsUnsigned(0,&canReadValue) == 0; + return canReadValue && isZero; +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) +{ + const uint32_t type_info = GetTypeInfo (); + if (type_info & ClangASTType::eTypeIsArray) + return GetSyntheticArrayMemberFromArray(index, can_create); + + if (type_info & ClangASTType::eTypeIsPointer) + return GetSyntheticArrayMemberFromPointer(index, can_create); + + return ValueObjectSP(); + +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", 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; +} + +// 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::GetSyntheticArrayMemberFromArray (size_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsArrayType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%zu]", 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) + { + // 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, + GetClangType(), + index_const_str, + GetByteSize(), + 0, + to-from+1, + from, + false, + false, + eAddressTypeInvalid); + + // 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 ClangASTType& type, bool can_create) +{ + + ValueObjectSP synthetic_child_sp; + + char name_str[64]; + snprintf(name_str, sizeof(name_str), "@%i", offset); + ConstString name_const_str(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 ValueObjectSP(); + + ValueObjectChild *synthetic_child = new ValueObjectChild(*this, + type, + name_const_str, + type.GetByteSize(), + offset, + 0, + 0, + false, + false, + eAddressTypeInvalid); + 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; +} + +// 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, + NULL, NULL, NULL, + GetValueForExpressionPathOptions().DontAllowSyntheticChildren()); + + // 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 == false) + return; + + TargetSP target_sp(GetTargetSP()); + if (target_sp && (target_sp->GetEnableSyntheticValue() == false || target_sp->GetSuppressSyntheticValue() == true)) + { + m_synthetic_value = NULL; + return; + } + + lldb::SyntheticChildrenSP current_synth_sp(m_synthetic_children_sp); + + if (!UpdateFormatsIfNeeded() && m_synthetic_value) + return; + + if (m_synthetic_children_sp.get() == NULL) + 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 == NULL) + { + 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 == false) + 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() == NULL) + return false; + + CalculateSyntheticValue(true); + + if (m_synthetic_value) + return true; + else + return false; +} + +bool +ValueObject::GetBaseClassPath (Stream &s) +{ + if (IsBaseClass()) + { + bool parent_had_base_class = GetParent() && GetParent()->GetBaseClassPath (s); + ClangASTType clang_type = GetClangType(); + std::string cxx_class_name; + bool this_had_base_class = clang_type.GetCXXClassName (cxx_class_name); + if (this_had_base_class) + { + if (parent_had_base_class) + s.PutCString("::"); + s.PutCString(cxx_class_name.c_str()); + } + 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 NULL; +} + +void +ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) +{ + 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) + { + ClangASTType non_base_class_parent_clang_type = non_base_class_parent->GetClangType(); + if (non_base_class_parent_clang_type) + { + if (parent && parent->IsDereferenceOfParent() && epformat == eGetExpressionPathFormatHonorPointers) + { + s.PutCString("->"); + } + else + { + const uint32_t non_base_class_parent_type_info = non_base_class_parent_clang_type.GetTypeInfo(); + + if (non_base_class_parent_type_info & ClangASTType::eTypeIsPointer) + { + s.PutCString("->"); + } + else if ((non_base_class_parent_type_info & ClangASTType::eTypeHasChildren) && + !(non_base_class_parent_type_info & ClangASTType::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(const char* expression, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + + const char* dummy_first_unparsed; + 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, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + 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) + { + Error 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) + { + Error 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 +} + +int +ValueObject::GetValuesForExpressionPath(const char* expression, + ValueObjectListSP& list, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + const char* dummy_first_unparsed; + ExpressionPathScanEndReason dummy_reason_to_stop; + ExpressionPathEndResultType dummy_final_value_type; + ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + + ValueObjectSP ret_val = GetValueForExpressionPath_Impl(expression, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + 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 (!ret_val.get()) // if there are errors, I add nothing to the list + return 0; + + if ( (reason_to_stop ? *reason_to_stop : dummy_reason_to_stop) != eExpressionPathScanEndReasonArrayRangeOperatorMet) + { + // I need not expand a range, just post-process the final value and return + if (!final_task_on_target || *final_task_on_target == ValueObject::eExpressionPathAftermathNothing) + { + list->Append(ret_val); + return 1; + } + 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 == ValueObject::eExpressionPathAftermathDereference) + { + Error 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 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + if (*final_task_on_target == ValueObject::eExpressionPathAftermathTakeAddress) + { + Error 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 0; + } + else + { + *final_task_on_target = ValueObject::eExpressionPathAftermathNothing; + list->Append(final_value); + return 1; + } + } + } + } + else + { + return ExpandArraySliceExpression(first_unparsed ? *first_unparsed : dummy_first_unparsed, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + ret_val, + list, + 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); + } + // in any non-covered case, just do the obviously right thing + list->Append(ret_val); + return 1; +} + +ValueObjectSP +ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + ValueObjectSP root = GetSP(); + + if (!root.get()) + return ValueObjectSP(); + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + return root; + } + + switch (*expression_cstr) + { + case '-': + { + if (options.m_check_dot_vs_arrow_syntax && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrowInsteadOfDot; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden + root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && + options.m_no_fragile_ivar) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonFragileIVarNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (expression_cstr[1] != '>') + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip the - + } + case '.': // or fallthrough from -> + { + if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' && + root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDotInsteadOfArrow; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + expression_cstr++; // skip . + const char *next_separator = strpbrk(expression_cstr+1,"-.["); + ConstString child_name; + if (!next_separator) // if no other separator just expand this last layer + { + child_name.SetCString (expression_cstr); + ValueObjectSP child_valobj_sp = root->GetChildMemberWithName(child_name, true); + + if (child_valobj_sp.get()) // we know we are done, so just return + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // 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 + { + *first_unparsed = ""; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + return child_valobj_sp; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // other layers do expand + { + child_name.SetCStringWithLength(expression_cstr, next_separator - expression_cstr); + 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; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else if (options.m_no_synthetic_children == false) // let's try with synthetic children + { + if (root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + + child_valobj_sp = root->GetSyntheticValue(true); + if (child_valobj_sp) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + + // 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; + *first_unparsed = next_separator; + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + break; + } + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer) && !root_clang_type_info.Test(ClangASTType::eTypeIsVector)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar... + { + if (options.m_no_synthetic_children) // ...only chance left is synthetic + { + *first_unparsed = expression_cstr; + *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 + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // even if something follows, we cannot expand unbounded ranges, just let the caller do it + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeUnboundedRange; + return root; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); + if (!child_valobj_sp) + child_valobj_sp = root->GetSyntheticArrayMemberFromArray(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; + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + if (root->GetClangType().GetMinimumLanguage() == eLanguageTypeObjC + && pointee_clang_type_info.AllClear(ClangASTType::eTypeIsPointer) + && root->HasSyntheticValue() + && options.m_no_synthetic_children == false) + { + root = root->GetSyntheticValue()->GetChildAtIndex(index, true); + } + else + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::eTypeIsVector)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else if (options.m_no_synthetic_children == false) + { + if (root->HasSyntheticValue()) + root = root->GetSyntheticValue(); + else if (!root->IsSynthetic()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + // if we are here, then root itself is a synthetic VO.. should be good to go + + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonSyntheticValueMissing; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *final_result = ValueObject::eExpressionPathEndResultTypePlain; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonBitfieldRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBitfield; + return root; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonArrayRangeOperatorMet; + *final_result = ValueObject::eExpressionPathEndResultTypeBoundedRange; + return root; + } + } + break; + } + default: // some non-separator is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return ValueObjectSP(); + break; + } + } + } +} + +int +ValueObject::ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + ValueObjectSP root, + ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + if (!root.get()) + return 0; + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + ClangASTType root_clang_type = root->GetClangType(); + ClangASTType pointee_clang_type; + Flags pointee_clang_type_info; + Flags root_clang_type_info(root_clang_type.GetTypeInfo(&pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info.Reset(pointee_clang_type.GetTypeInfo()); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEndOfString; + list->Append(root); + return 1; + } + + switch (*expression_cstr) + { + case '[': + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray) && !root_clang_type_info.Test(ClangASTType::eTypeIsPointer)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else if (!options.m_allow_bitfields_syntax) // if this is a scalar, check that we can expand bitfields + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // expand this into list + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + const size_t max_index = root->GetNumChildren() - 1; + for (size_t index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonEmptyRangeNotAllowed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTType::eTypeIsArray)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = eExpressionPathAftermathNothing; + continue; + } + } + else + { + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else /*if (ClangASTContext::IsScalarType(root_clang_type))*/ + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTType::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTType::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_clang_type_info.Test(ClangASTType::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonDereferencingFailed; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + } + else + { + *what_next = ValueObject::eExpressionPathAftermathNothing; + continue; + } + } + else + { + for (unsigned long index = index_lower; + index <= index_higher; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = end+1; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorExpanded; + *final_result = ValueObject::eExpressionPathEndResultTypeValueObjectList; + return index_higher-index_lower+1; // tell me number of items I added to the VOList + } + } + break; + } + default: // some non-[ separator, or something entirely wrong, is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eExpressionPathScanEndReasonUnexpectedSymbol; + *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; + return 0; + break; + } + } + } +} + +static void +DumpValueObject_Impl (Stream &s, + ValueObject *valobj, + const ValueObject::DumpValueObjectOptions& options, + uint32_t ptr_depth, + uint32_t curr_depth) +{ + if (valobj) + { + bool update_success = valobj->UpdateValueIfNeeded (true); + + const char *root_valobj_name = + options.m_root_valobj_name.empty() ? + valobj->GetName().AsCString() : + options.m_root_valobj_name.c_str(); + + if (update_success && options.m_use_dynamic != eNoDynamicValues) + { + ValueObject *dynamic_value = valobj->GetDynamicValue(options.m_use_dynamic).get(); + if (dynamic_value) + valobj = dynamic_value; + } + + ClangASTType clang_type = valobj->GetClangType(); + const Flags type_flags (clang_type.GetTypeInfo ()); + const char *err_cstr = NULL; + const bool has_children = type_flags.Test (ClangASTType::eTypeHasChildren); + const bool has_value = type_flags.Test (ClangASTType::eTypeHasValue); + + const bool print_valobj = options.m_flat_output == false || has_value; + + if (print_valobj) + { + if (options.m_show_location) + { + s.Printf("%s: ", valobj->GetLocationAsCString()); + } + + s.Indent(); + + bool show_type = true; + // if we are at the root-level and been asked to hide the root's type, then hide it + if (curr_depth == 0 && 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 = options.m_show_types || (curr_depth == 0 && !options.m_flat_output); + + if (show_type) + { + // Some ValueObjects don't have types (like registers sets). Only print + // the type if there is one to print + ConstString qualified_type_name(valobj->GetQualifiedTypeName()); + if (qualified_type_name) + s.Printf("(%s) ", qualified_type_name.GetCString()); + } + + if (options.m_flat_output) + { + // If we are showing types, also qualify the C++ base classes + const bool qualify_cxx_base_classes = options.m_show_types; + if (!options.m_hide_name) + { + valobj->GetExpressionPath(s, qualify_cxx_base_classes); + s.PutCString(" ="); + } + } + else if (!options.m_hide_name) + { + const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); + s.Printf ("%s =", name_cstr); + } + + if (!options.m_scope_already_checked && !valobj->IsInScope()) + { + err_cstr = "out of scope"; + } + } + + std::string summary_str; + std::string value_str; + const char *val_cstr = NULL; + const char *sum_cstr = NULL; + TypeSummaryImpl* entry = options.m_summary_sp ? options.m_summary_sp.get() : valobj->GetSummaryFormat().get(); + + if (options.m_omit_summary_depth > 0) + entry = NULL; + + bool is_nil = valobj->IsObjCNil(); + + if (err_cstr == NULL) + { + if (options.m_format != eFormatDefault && options.m_format != valobj->GetFormat()) + { + valobj->GetValueAsCString(options.m_format, + value_str); + } + else + { + val_cstr = valobj->GetValueAsCString(); + if (val_cstr) + value_str = val_cstr; + } + err_cstr = valobj->GetError().AsCString(); + } + + if (err_cstr) + { + s.Printf (" <%s>\n", err_cstr); + } + else + { + const bool is_ref = type_flags.Test (ClangASTType::eTypeIsReference); + if (print_valobj) + { + if (is_nil) + sum_cstr = "nil"; + else if (options.m_omit_summary_depth == 0) + { + if (options.m_summary_sp) + { + valobj->GetSummaryAsCString(entry, summary_str); + sum_cstr = summary_str.c_str(); + } + else + sum_cstr = valobj->GetSummaryAsCString(); + } + + // 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) + if (!is_nil && !value_str.empty() && (entry == NULL || (entry->DoesPrintValue() || options.m_format != eFormatDefault) || sum_cstr == NULL) && !options.m_hide_value) + s.Printf(" %s", value_str.c_str()); + + if (sum_cstr) + s.Printf(" %s", sum_cstr); + + // let's avoid the overly verbose no description error for a nil thing + if (options.m_use_objc && !is_nil) + { + if (!options.m_hide_value || !options.m_hide_name) + s.Printf(" "); + const char *object_desc = valobj->GetObjectDescription(); + if (object_desc) + s.Printf("%s\n", object_desc); + else + s.Printf ("[no Objective-C description available]\n"); + return; + } + } + + if (curr_depth < 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). + bool print_children = true; + + // Use a new temporary pointer depth in case we override the + // current pointer depth below... + uint32_t curr_ptr_depth = ptr_depth; + + const bool is_ptr = type_flags.Test (ClangASTType::eTypeIsPointer); + 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 (valobj->GetPointerValue (&ptr_address_type) == 0) + print_children = false; + + else if (is_ref && curr_depth == 0) + { + // 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... + curr_ptr_depth = 1; + } + + if (curr_ptr_depth == 0) + print_children = false; + } + + if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) + { + ValueObjectSP synth_valobj_sp = valobj->GetSyntheticValue (options.m_use_synthetic); + ValueObject* synth_valobj = (synth_valobj_sp ? synth_valobj_sp.get() : valobj); + + size_t num_children = synth_valobj->GetNumChildren(); + bool print_dotdotdot = false; + if (num_children) + { + if (options.m_flat_output) + { + if (print_valobj) + s.EOL(); + } + else + { + if (print_valobj) + s.PutCString(is_ref ? ": {\n" : " {\n"); + s.IndentMore(); + } + + const size_t max_num_children = valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + + if (num_children > max_num_children && !options.m_ignore_cap) + { + num_children = max_num_children; + print_dotdotdot = true; + } + + ValueObject::DumpValueObjectOptions child_options(options); + child_options.SetFormat(options.m_format).SetSummary().SetRootValueObjectName(); + child_options.SetScopeChecked(true).SetHideName(options.m_hide_name).SetHideValue(options.m_hide_value) + .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 ? child_options.m_omit_summary_depth - 1 : 0); + for (size_t idx=0; idxGetChildAtIndex(idx, true)); + if (child_sp.get()) + { + DumpValueObject_Impl (s, + child_sp.get(), + child_options, + (is_ptr || is_ref) ? curr_ptr_depth - 1 : curr_ptr_depth, + curr_depth + 1); + } + } + + if (!options.m_flat_output) + { + if (print_dotdotdot) + { + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + target->GetDebugger().GetCommandInterpreter().ChildrenTruncated(); + s.Indent("...\n"); + } + s.IndentLess(); + s.Indent("}\n"); + } + } + else if (has_children) + { + // Aggregate, no children... + if (print_valobj) + s.PutCString(" {}\n"); + } + else + { + if (print_valobj) + s.EOL(); + } + + } + else + { + s.EOL(); + } + } + else + { + if (has_children && print_valobj) + { + s.PutCString("{...}\n"); + } + } + } + } +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj) +{ + if (log && valobj) + return LogValueObject (log, valobj, DumpValueObjectOptions::DefaultOptions()); +} + +void +ValueObject::LogValueObject (Log *log, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + if (log && valobj) + { + StreamString s; + ValueObject::DumpValueObject (s, valobj, options); + if (s.GetSize()) + log->PutCString(s.GetData()); + } +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj) +{ + + if (!valobj) + return; + + DumpValueObject_Impl(s, + valobj, + DumpValueObjectOptions::DefaultOptions(), + 0, + 0); +} + +void +ValueObject::DumpValueObject (Stream &s, + ValueObject *valobj, + const DumpValueObjectOptions& options) +{ + DumpValueObject_Impl(s, + valobj, + options, + options.m_max_ptr_depth, // max pointer depth allowed, we will go down from here + 0 // current object depth is 0 since we are just starting + ); +} + +ValueObjectSP +ValueObject::CreateConstantValue (const 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(), + GetClangType(), + name, + data, + GetAddressOf()); + } + + if (!valobj_sp) + { + valobj_sp = ValueObjectConstResult::Create (NULL, m_error); + } + return valobj_sp; +} + +ValueObjectSP +ValueObject::Dereference (Error &error) +{ + if (m_deref_valobj) + return m_deref_valobj->GetSP(); + + const bool is_pointer_type = IsPointerType(); + if (is_pointer_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; + ClangASTType clang_type = GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + GetName().GetCString(), + 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); + if (child_clang_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_clang_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); + } + } + + if (m_deref_valobj) + { + error.Clear(); + return m_deref_valobj->GetSP(); + } + else + { + StreamString strm; + GetExpressionPath(strm, true); + + if (is_pointer_type) + error.SetErrorStringWithFormat("dereference failed: (%s) %s", GetTypeName().AsCString(""), strm.GetString().c_str()); + else + error.SetErrorStringWithFormat("not a pointer type: (%s) %s", GetTypeName().AsCString(""), strm.GetString().c_str()); + return ValueObjectSP(); + } +} + +ValueObjectSP +ValueObject::AddressOf (Error &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) + { + switch (address_type) + { + case eAddressTypeInvalid: + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_strm.GetString().c_str()); + } + break; + + case eAddressTypeFile: + case eAddressTypeLoad: + case eAddressTypeHost: + { + ClangASTType clang_type = GetClangType(); + if (clang_type) + { + std::string name (1, '&'); + name.append (m_name.AsCString("")); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_addr_of_valobj_sp = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + clang_type.GetPointerType(), + ConstString (name.c_str()), + addr, + eAddressTypeInvalid, + m_data.GetAddressByteSize()); + } + } + break; + } + } + else + { + StreamString expr_path_strm; + GetExpressionPath(expr_path_strm, true); + error.SetErrorStringWithFormat("'%s' doesn't have a valid address", expr_path_strm.GetString().c_str()); + } + + return m_addr_of_valobj_sp; +} + +ValueObjectSP +ValueObject::Cast (const ClangASTType &clang_ast_type) +{ + return ValueObjectCast::Create (*this, GetName(), clang_ast_type); +} + +ValueObjectSP +ValueObject::CastPointerType (const char *name, ClangASTType &clang_ast_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, + clang_ast_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), + m_first_update (true) +{ +} + +ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected): + m_mod_id(), + m_exe_ctx_ref(), + m_needs_update (true), + m_first_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), + m_first_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() +{ + + // Start with the target, if it is NULL, then we're obviously not going to get any further: + ExecutionContext exe_ctx(m_exe_ctx_ref.Lock()); + + if (exe_ctx.GetTargetPtr() == NULL) + return false; + + // If we don't have a process nothing can change. + Process *process = exe_ctx.GetProcessPtr(); + if (process == NULL) + 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 (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_first_update = false; + 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 = NULL; + } +} + +SymbolContextScope * +ValueObject::GetSymbolContextScope() +{ + if (m_parent) + { + if (!m_parent->IsPointerOrReferenceType()) + return m_parent->GetSymbolContextScope(); + } + return NULL; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromExpression (const char* name, + const char* expression, + const ExecutionContext& exe_ctx) +{ + lldb::ValueObjectSP retval_sp; + lldb::TargetSP target_sp(exe_ctx.GetTargetSP()); + if (!target_sp) + return retval_sp; + if (!expression || !*expression) + return retval_sp; + target_sp->EvaluateExpression (expression, + exe_ctx.GetFrameSP().get(), + retval_sp); + if (retval_sp && name && *name) + retval_sp->SetName(ConstString(name)); + return retval_sp; +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromAddress (const char* name, + uint64_t address, + const ExecutionContext& exe_ctx, + ClangASTType type) +{ + if (type) + { + ClangASTType 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, + lldb::endian::InlHostByteOrder(), + exe_ctx.GetAddressByteSize())); + if (ptr_result_valobj_sp) + { + ptr_result_valobj_sp->GetValue().SetValueType(Value::eValueTypeLoadAddress); + Error err; + ptr_result_valobj_sp = ptr_result_valobj_sp->Dereference(err); + if (ptr_result_valobj_sp && name && *name) + ptr_result_valobj_sp->SetName(ConstString(name)); + } + return ptr_result_valobj_sp; + } + } + return lldb::ValueObjectSP(); +} + +lldb::ValueObjectSP +ValueObject::CreateValueObjectFromData (const char* name, + DataExtractor& data, + const ExecutionContext& exe_ctx, + ClangASTType 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 && *name) + 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; + ValueObject* parent = m_parent; + if (!parent) + return (m_root = this); + while (parent->m_parent) + { + if (parent->m_root) + return (m_root = parent->m_root); + parent = parent->m_parent; + } + return (m_root = parent); +} + +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; +} diff --git a/source/Core/ValueObjectCast.cpp b/source/Core/ValueObjectCast.cpp new file mode 100644 index 00000000000..4f4f8cc681d --- /dev/null +++ b/source/Core/ValueObjectCast.cpp @@ -0,0 +1,129 @@ +//===-- ValueObjectDynamicValue.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectCast.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ClangASTType.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/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb_private; + +lldb::ValueObjectSP +ValueObjectCast::Create (ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type) +{ + ValueObjectCast *cast_valobj_ptr = new ValueObjectCast (parent, name, cast_type); + return cast_valobj_ptr->GetSP(); +} + +ValueObjectCast::ValueObjectCast +( + ValueObject &parent, + const ConstString &name, + const ClangASTType &cast_type +) : + ValueObject(parent), + m_cast_type (cast_type) +{ + SetName (name); + //m_value.SetContext (Value::eContextTypeClangType, cast_type.GetOpaqueQualType()); + m_value.SetClangType (cast_type); +} + +ValueObjectCast::~ValueObjectCast() +{ +} + +ClangASTType +ValueObjectCast::GetClangTypeImpl () +{ + return m_cast_type; +} + +size_t +ValueObjectCast::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +uint64_t +ValueObjectCast::GetByteSize() +{ + return m_value.GetValueByteSize(NULL); +} + +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(); + ClangASTType clang_type (GetClangType()); + //m_value.SetContext (Value::eContextTypeClangType, clang_type); + m_value.SetClangType (clang_type); + SetAddressTypeOfChildren(m_parent->GetAddressTypeOfChildren()); + if (clang_type.IsAggregateType ()) + { + // 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/source/Core/ValueObjectChild.cpp b/source/Core/ValueObjectChild.cpp new file mode 100644 index 00000000000..23add1ccf0e --- /dev/null +++ b/source/Core/ValueObjectChild.cpp @@ -0,0 +1,234 @@ +//===-- ValueObjectChild.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectChild.h" + +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.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/Target.h" + +using namespace lldb_private; + +ValueObjectChild::ValueObjectChild +( + ValueObject &parent, + const ClangASTType &clang_type, + const 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 +) : + ValueObject (parent), + m_clang_type (clang_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_name = name; + SetAddressTypeOfChildren(child_ptr_or_ref_addr_type); +} + +ValueObjectChild::~ValueObjectChild() +{ +} + +lldb::ValueType +ValueObjectChild::GetValueType() const +{ + return m_parent->GetValueType(); +} + +size_t +ValueObjectChild::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +ConstString +ValueObjectChild::GetTypeName() +{ + if (m_type_name.IsEmpty()) + { + m_type_name = GetClangType().GetConstTypeName (); + if (m_type_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = m_type_name.AsCString(); + if (clang_type_name) + { + std::vector bitfield_type_name (strlen(clang_type_name) + 32, 0); + ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", clang_type_name, m_bitfield_bit_size); + m_type_name.SetCString(&bitfield_type_name.front()); + } + } + } + } + return m_type_name; +} + +ConstString +ValueObjectChild::GetQualifiedTypeName() +{ + ConstString qualified_name = GetClangType().GetConstTypeName(); + if (qualified_name) + { + if (m_bitfield_bit_size > 0) + { + const char *clang_type_name = qualified_name.AsCString(); + if (clang_type_name) + { + std::vector bitfield_type_name (strlen(clang_type_name) + 32, 0); + ::snprintf (&bitfield_type_name.front(), bitfield_type_name.size(), "%s:%u", clang_type_name, m_bitfield_bit_size); + qualified_name.SetCString(&bitfield_type_name.front()); + } + } + } + return qualified_name; +} + +bool +ValueObjectChild::UpdateValue () +{ + m_error.Clear(); + SetValueIsValid (false); + ValueObject* parent = m_parent; + if (parent) + { + if (parent->UpdateValueIfNeeded(false)) + { + m_value.SetClangType(GetClangType()); + + // 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); + + if (parent->GetClangType().IsPointerOrReferenceType ()) + { + 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() == true) + m_value.SetValueType (Value::eValueTypeLoadAddress); + else + m_value.SetValueType(Value::eValueTypeFileAddress); + } + break; + case eAddressTypeLoad: + m_value.SetValueType (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: + // TODO: What if this is a register value? Do we try and + // extract the child value from within the parent data? + // Probably... + default: + m_error.SetErrorString ("parent has invalid value."); + break; + } + } + + if (m_error.Success()) + { + ExecutionContext exe_ctx (GetExecutionContextRef().Lock()); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); + } + } + 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/source/Core/ValueObjectConstResult.cpp b/source/Core/ValueObjectConstResult.cpp new file mode 100644 index 00000000000..d6d86381358 --- /dev/null +++ b/source/Core/ValueObjectConstResult.cpp @@ -0,0 +1,354 @@ +//===-- ValueObjectConstResult.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResult.h" + +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResultChild.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectDynamicValue.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.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/Target.h" + +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 ClangASTType &clang_type, + const ConstString &name, + const DataExtractor &data, + lldb::addr_t address +) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + data, + address))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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.SetClangType(clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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, + clang_type, + name, + data_sp, + data_byte_order, + data_addr_size, + address))->GetSP(); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + Value &value, + const ConstString &name) +{ + return (new ValueObjectConstResult (exe_scope, value, name))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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, clang_type); + m_value.SetClangType (clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const ConstString &name, + lldb::addr_t address, + AddressType address_type, + uint32_t addr_byte_size) +{ + return (new ValueObjectConstResult (exe_scope, + clang_type, + name, + address, + address_type, + addr_byte_size))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const ClangASTType &clang_type, + const 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, clang_type); + m_value.SetClangType (clang_type); + m_name = name; + SetIsConstant (); + SetValueIsValid(true); + SetAddressTypeOfChildren(eAddressTypeLoad); +} + +ValueObjectSP +ValueObjectConstResult::Create +( + ExecutionContextScope *exe_scope, + const Error& error +) +{ + return (new ValueObjectConstResult (exe_scope, + error))->GetSP(); +} + +ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope, + const Error& 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, + const ConstString &name) : + ValueObject (exe_scope), + m_type_name (), + m_byte_size (0), + m_impl(this) +{ + m_value = value; + m_value.GetData(m_data); +} + +ValueObjectConstResult::~ValueObjectConstResult() +{ +} + +ClangASTType +ValueObjectConstResult::GetClangTypeImpl() +{ + return m_value.GetClangType(); +} + +lldb::ValueType +ValueObjectConstResult::GetValueType() const +{ + return eValueTypeConstResult; +} + +uint64_t +ValueObjectConstResult::GetByteSize() +{ + if (m_byte_size == 0) + m_byte_size = GetClangType().GetByteSize(); + return m_byte_size; +} + +void +ValueObjectConstResult::SetByteSize (size_t size) +{ + m_byte_size = size; +} + +size_t +ValueObjectConstResult::CalculateNumChildren() +{ + return GetClangType().GetNumChildren (true); +} + +ConstString +ValueObjectConstResult::GetTypeName() +{ + if (m_type_name.IsEmpty()) + m_type_name = GetClangType().GetConstTypeName (); + return m_type_name; +} + +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 (Error &error) +{ + return m_impl.Dereference(error); +} + +lldb::ValueObjectSP +ValueObjectConstResult::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + return m_impl.GetSyntheticChildAtOffset(offset, type, can_create); +} + +lldb::ValueObjectSP +ValueObjectConstResult::AddressOf (Error &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(); +} + diff --git a/source/Core/ValueObjectConstResultChild.cpp b/source/Core/ValueObjectConstResultChild.cpp new file mode 100644 index 00000000000..64425ea5096 --- /dev/null +++ b/source/Core/ValueObjectConstResultChild.cpp @@ -0,0 +1,80 @@ +//===-- ValueObjectConstResultChild.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResultChild.h" + +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTContext.h" + +using namespace lldb_private; + +ValueObjectConstResultChild::ValueObjectConstResultChild +( + ValueObject &parent, + const ClangASTType &clang_type, + const 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 +) : + ValueObjectChild (parent, + clang_type, + name, + byte_size, + byte_offset, + bitfield_bit_size, + bitfield_bit_offset, + is_base_class, + is_deref_of_parent, + eAddressTypeLoad), + m_impl(this) +{ + m_name = name; +} + +ValueObjectConstResultChild::~ValueObjectConstResultChild() +{ +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::Dereference (Error &error) +{ + return m_impl.Dereference(error); +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create) +{ + return m_impl.GetSyntheticChildAtOffset(offset, type, can_create); +} + +lldb::ValueObjectSP +ValueObjectConstResultChild::AddressOf (Error &error) +{ + return m_impl.AddressOf(error); +} + +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); +} diff --git a/source/Core/ValueObjectConstResultImpl.cpp b/source/Core/ValueObjectConstResultImpl.cpp new file mode 100644 index 00000000000..e0757f60cdb --- /dev/null +++ b/source/Core/ValueObjectConstResultImpl.cpp @@ -0,0 +1,236 @@ +//===-- ValueObjectConstResultImpl.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectConstResultImpl.h" + +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectConstResultChild.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTType.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/Target.h" + +using namespace lldb; +using namespace lldb_private; + +// this macro enables a simpler implementation for some method calls in this object that relies only upon +// ValueObject knowning how to set the address type of its children correctly. the alternative implementation +// relies on being able to create a target copy of the frozen object, which makes it less bug-prone but less +// efficient as well. once we are confident the faster implementation is bug-free, this macro (and the slower +// implementations) can go +#define TRIVIAL_IMPL 1 + +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::DerefOnTarget() +{ + if (m_load_addr_backend.get() == NULL) + { + lldb::addr_t tgt_address = m_impl_backend->GetPointerValue(); + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + m_load_addr_backend = ValueObjectConstResult::Create (exe_ctx.GetBestExecutionContextScope(), + m_impl_backend->GetClangType(), + m_impl_backend->GetName(), + tgt_address, + eAddressTypeLoad, + exe_ctx.GetAddressByteSize()); + } + return m_load_addr_backend; +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::Dereference (Error &error) +{ + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::Dereference(error); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget()) + return DerefOnTarget()->Dereference(error); + else + return m_impl_backend->ValueObject::Dereference(error); +#endif +} + +ValueObject * +ValueObjectConstResultImpl::CreateChildAtIndex (size_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + if (m_impl_backend == NULL) + return NULL; + + m_impl_backend->UpdateValueIfNeeded(false); + + ValueObjectConstResultChild *valobj = NULL; + + 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; + + const bool transparent_pointers = synthetic_array_member == false; + ClangASTType clang_type = m_impl_backend->GetClangType(); + ClangASTType child_clang_type; + + ExecutionContext exe_ctx (m_impl_backend->GetExecutionContextRef()); + + child_clang_type = clang_type.GetChildClangTypeAtIndex (&exe_ctx, + m_impl_backend->GetName().GetCString(), + 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); + if (child_clang_type && child_byte_size) + { + 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_clang_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); + valobj->m_impl.SetLiveAddress(m_live_address+child_byte_offset); + } + + return valobj; +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::GetSyntheticChildAtOffset (uint32_t offset, const ClangASTType& type, bool can_create) +{ + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget()) + return DerefOnTarget()->GetSyntheticChildAtOffset(offset, type, can_create); + else + return m_impl_backend->ValueObject::GetSyntheticChildAtOffset(offset, type, can_create); +#endif +} + +lldb::ValueObjectSP +ValueObjectConstResultImpl::AddressOf (Error &error) +{ + if (m_address_of_backend.get() != NULL) + return m_address_of_backend; + + if (m_impl_backend == NULL) + return lldb::ValueObjectSP(); + if (m_live_address != LLDB_INVALID_ADDRESS) + { + ClangASTType clang_type(m_impl_backend->GetClangType()); + + 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(), + clang_type.GetPointerType(), + ConstString(new_name.c_str()), + buffer, + lldb::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 lldb::ValueObjectSP(); +} + +lldb::addr_t +ValueObjectConstResultImpl::GetAddressOf (bool scalar_is_load_address, + AddressType *address_type) +{ + + if (m_impl_backend == NULL) + 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 == NULL) + return 0; +#if defined (TRIVIAL_IMPL) && TRIVIAL_IMPL == 1 + return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); +#else + m_impl_backend->UpdateValueIfNeeded(false); + + if (NeedsDerefOnTarget() && m_impl_backend->IsPointerType()) + return DerefOnTarget()->GetPointeeData(data, item_idx, item_count); + else + return m_impl_backend->ValueObject::GetPointeeData(data, item_idx, item_count); +#endif +} diff --git a/source/Core/ValueObjectDynamicValue.cpp b/source/Core/ValueObjectDynamicValue.cpp new file mode 100644 index 00000000000..977cc4cd313 --- /dev/null +++ b/source/Core/ValueObjectDynamicValue.cpp @@ -0,0 +1,372 @@ +//===-- ValueObjectDynamicValue.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectDynamicValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" + +#include "lldb/Symbol/ClangASTType.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/LanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +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(); +} + +ClangASTType +ValueObjectDynamicValue::GetClangTypeImpl () +{ + if (m_dynamic_type_info.HasTypeSP()) + return m_value.GetClangType(); + else + return m_parent->GetClangType(); +} + +ConstString +ValueObjectDynamicValue::GetTypeName() +{ + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetConstTypeName(); + if (m_dynamic_type_info.HasName()) + return m_dynamic_type_info.GetName(); + } + return m_parent->GetTypeName(); +} + +ConstString +ValueObjectDynamicValue::GetQualifiedTypeName() +{ + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetConstQualifiedTypeName (); + if (m_dynamic_type_info.HasName()) + return m_dynamic_type_info.GetName(); + } + return m_parent->GetTypeName(); +} + +size_t +ValueObjectDynamicValue::CalculateNumChildren() +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_dynamic_type_info.HasTypeSP()) + return GetClangType().GetNumChildren (true); + else + return m_parent->GetNumChildren(); +} + +uint64_t +ValueObjectDynamicValue::GetByteSize() +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_dynamic_type_info.HasTypeSP()) + return m_value.GetValueByteSize(NULL); + 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; + + lldb::LanguageType known_type = m_parent->GetObjectRuntimeLanguage(); + if (known_type != lldb::eLanguageTypeUnknown && known_type != lldb::eLanguageTypeC) + { + LanguageRuntime *runtime = process->GetLanguageRuntime (known_type); + if (runtime) + found_dynamic_type = runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + } + else + { + LanguageRuntime *cpp_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeC_plus_plus); + if (cpp_runtime) + found_dynamic_type = cpp_runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + + if (!found_dynamic_type) + { + LanguageRuntime *objc_runtime = process->GetLanguageRuntime (lldb::eLanguageTypeObjC); + if (objc_runtime) + found_dynamic_type = objc_runtime->GetDynamicTypeAndAddress (*m_parent, m_use_dynamic, class_type_or_name, dynamic_address); + } + } + + // 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 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; + } + + ClangASTType corrected_type; + if (m_dynamic_type_info.HasTypeSP()) + { + // 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... + ClangASTType orig_type = m_dynamic_type_info.GetTypeSP()->GetClangForwardType(); + corrected_type = orig_type; + if (m_parent->IsPointerType()) + corrected_type = orig_type.GetPointerType (); + else if (m_parent->IsPointerOrReferenceType()) + corrected_type = orig_type.GetLValueReferenceType (); + } + else /*if (m_dynamic_type_info.HasName())*/ + { + // If we are here we need to adjust our dynamic type name to include the correct & or * symbol + std::string type_name_buf (m_dynamic_type_info.GetName().GetCString()); + if (m_parent->IsPointerType()) + type_name_buf.append(" *"); + else if (m_parent->IsPointerOrReferenceType()) + type_name_buf.append(" &"); + corrected_type = m_parent->GetClangType(); + m_dynamic_type_info.SetName(type_name_buf.c_str()); + } + + //m_value.SetContext (Value::eContextTypeClangType, corrected_type); + m_value.SetClangType (corrected_type); + + // Our address is the location of the dynamic type stored in memory. It isn't a load address, + // because we aren't pointing to the LOCATION that stores the pointer to us, we're pointing to us... + m_value.SetValueType(Value::eValueTypeScalar); + + if (has_changed_type && log) + log->Printf("[%s %p] has a new dynamic type %s", + GetName().GetCString(), + 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 (GetClangType().IsAggregateType ()) + { + // 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, Error& 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, Error &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; +} diff --git a/source/Core/ValueObjectList.cpp b/source/Core/ValueObjectList.cpp new file mode 100644 index 00000000000..180d4a0eaf1 --- /dev/null +++ b/source/Core/ValueObjectList.cpp @@ -0,0 +1,166 @@ +//===-- ValueObjectList.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObjectList.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectList::ValueObjectList () : + m_value_objects() +{ +} + +ValueObjectList::ValueObjectList (const ValueObjectList &rhs) : + m_value_objects(rhs.m_value_objects) +{ +} + + +ValueObjectList::~ValueObjectList () +{ +} + +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/source/Core/ValueObjectMemory.cpp b/source/Core/ValueObjectMemory.cpp new file mode 100644 index 00000000000..42fd0e8fffb --- /dev/null +++ b/source/Core/ValueObjectMemory.cpp @@ -0,0 +1,275 @@ +//===-- ValueObjectMemory.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectMemory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.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/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +ValueObjectSP +ValueObjectMemory::Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp) +{ + return (new ValueObjectMemory (exe_scope, name, address, type_sp))->GetSP(); +} + +ValueObjectSP +ValueObjectMemory::Create (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + const ClangASTType &ast_type) +{ + return (new ValueObjectMemory (exe_scope, name, address, ast_type))->GetSP(); +} + +ValueObjectMemory::ValueObjectMemory (ExecutionContextScope *exe_scope, + const char *name, + const Address &address, + lldb::TypeSP &type_sp) : + ValueObject(exe_scope), + m_address (address), + m_type_sp(type_sp), + m_clang_type() +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_type_sp.get() != NULL); + 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, + const char *name, + const Address &address, + const ClangASTType &ast_type) : + ValueObject(exe_scope), + m_address (address), + m_type_sp(), + m_clang_type(ast_type) +{ + // Do not attempt to construct one of these objects with no variable! + assert (m_clang_type.GetASTContext()); + assert (m_clang_type.GetOpaqueQualType()); + + TargetSP target_sp (GetTargetSP()); + + SetName (ConstString(name)); +// m_value.SetContext(Value::eContextTypeClangType, m_clang_type.GetOpaqueQualType()); + m_value.SetClangType(m_clang_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() +{ +} + +ClangASTType +ValueObjectMemory::GetClangTypeImpl () +{ + if (m_type_sp) + return m_type_sp->GetClangForwardType(); + return m_clang_type; +} + +ConstString +ValueObjectMemory::GetTypeName() +{ + if (m_type_sp) + return m_type_sp->GetName(); + return m_clang_type.GetConstTypeName(); +} + +size_t +ValueObjectMemory::CalculateNumChildren() +{ + if (m_type_sp) + return m_type_sp->GetNumChildren(true); + const bool omit_empty_base_classes = true; + return m_clang_type.GetNumChildren (omit_empty_base_classes); +} + +uint64_t +ValueObjectMemory::GetByteSize() +{ + if (m_type_sp) + return m_type_sp->GetByteSize(); + return m_clang_type.GetByteSize (); +} + +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: + assert(!"Unhandled expression result value kind..."); + break; + + 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 (GetClangType().IsAggregateType()) + { + // 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_clang_type.GetOpaqueQualType()); + value.SetClangType(m_clang_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/source/Core/ValueObjectRegister.cpp b/source/Core/ValueObjectRegister.cpp new file mode 100644 index 00000000000..4f21457519e --- /dev/null +++ b/source/Core/ValueObjectRegister.cpp @@ -0,0 +1,431 @@ +//===-- ValueObjectRegister.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectRegister.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/TypeList.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" + +using namespace lldb; +using namespace lldb_private; + +#pragma mark ValueObjectRegisterContext + +ValueObjectRegisterContext::ValueObjectRegisterContext (ValueObject &parent, RegisterContextSP ®_ctx) : + ValueObject (parent), + m_reg_ctx_sp (reg_ctx) +{ + assert (reg_ctx); + m_name.SetCString("Registers"); + SetValueIsValid (true); +} + +ValueObjectRegisterContext::~ValueObjectRegisterContext() +{ +} + +ClangASTType +ValueObjectRegisterContext::GetClangTypeImpl () +{ + return ClangASTType(); +} + +ConstString +ValueObjectRegisterContext::GetTypeName() +{ + return ConstString(); +} + +ConstString +ValueObjectRegisterContext::GetQualifiedTypeName() +{ + return ConstString(); +} + +size_t +ValueObjectRegisterContext::CalculateNumChildren() +{ + return m_reg_ctx_sp->GetRegisterSetCount(); +} + +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() == NULL) + { + 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 = NULL; + + 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 ®_ctx_sp, uint32_t set_idx) +{ + return (new ValueObjectRegisterSet (exe_scope, reg_ctx_sp, set_idx))->GetSP(); +} + + +ValueObjectRegisterSet::ValueObjectRegisterSet (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx, uint32_t reg_set_idx) : + ValueObject (exe_scope), + m_reg_ctx_sp (reg_ctx), + m_reg_set (NULL), + 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() +{ +} + +ClangASTType +ValueObjectRegisterSet::GetClangTypeImpl () +{ + return ClangASTType(); +} + +ConstString +ValueObjectRegisterSet::GetTypeName() +{ + return ConstString(); +} + +ConstString +ValueObjectRegisterSet::GetQualifiedTypeName() +{ + return ConstString(); +} + +size_t +ValueObjectRegisterSet::CalculateNumChildren() +{ + const RegisterSet *reg_set = m_reg_ctx_sp->GetRegisterSet(m_reg_set_idx); + if (reg_set) + return reg_set->num_registers; + 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 == NULL) + 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 == NULL) + 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 = NULL; + 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 (const ConstString &name, bool can_create) +{ + ValueObject *valobj = NULL; + if (m_reg_ctx_sp && m_reg_set) + { + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoByName (name.AsCString()); + if (reg_info != NULL) + valobj = new ValueObjectRegister(*this, m_reg_ctx_sp, reg_info->kinds[eRegisterKindLLDB]); + } + if (valobj) + return valobj->GetSP(); + else + return ValueObjectSP(); +} + +size_t +ValueObjectRegisterSet::GetIndexOfChildWithName (const ConstString &name) +{ + if (m_reg_ctx_sp && m_reg_set) + { + const RegisterInfo *reg_info = m_reg_ctx_sp->GetRegisterInfoByName (name.AsCString()); + if (reg_info != NULL) + 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 ®_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_clang_type () +{ + assert (reg_ctx_sp.get()); + ConstructObject(reg_num); +} + +ValueObjectSP +ValueObjectRegister::Create (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx_sp, uint32_t reg_num) +{ + return (new ValueObjectRegister (exe_scope, reg_ctx_sp, reg_num))->GetSP(); +} + +ValueObjectRegister::ValueObjectRegister (ExecutionContextScope *exe_scope, lldb::RegisterContextSP ®_ctx, uint32_t reg_num) : + ValueObject (exe_scope), + m_reg_ctx_sp (reg_ctx), + m_reg_info (), + m_reg_value (), + m_type_name (), + m_clang_type () +{ + assert (reg_ctx); + ConstructObject(reg_num); +} + +ValueObjectRegister::~ValueObjectRegister() +{ +} + +ClangASTType +ValueObjectRegister::GetClangTypeImpl () +{ + if (!m_clang_type.IsValid()) + { + ExecutionContext exe_ctx (GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + Module *exe_module = target->GetExecutableModulePointer(); + if (exe_module) + { + m_clang_type = exe_module->GetClangASTContext().GetBuiltinTypeForEncodingAndBitSize (m_reg_info.encoding, + m_reg_info.byte_size * 8); + } + } + } + return m_clang_type; +} + +ConstString +ValueObjectRegister::GetTypeName() +{ + if (m_type_name.IsEmpty()) + m_type_name = GetClangType().GetConstTypeName (); + return m_type_name; +} + +size_t +ValueObjectRegister::CalculateNumChildren() +{ + return GetClangType().GetNumChildren(true); +} + +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 == NULL) + { + m_reg_ctx_sp.reset(); + m_reg_value.Clear(); + } + + + if (m_reg_ctx_sp) + { + 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); + return true; + } + } + } + + SetValueIsValid (false); + m_error.SetErrorToGenericError (); + return false; +} + +bool +ValueObjectRegister::SetValueFromCString (const char *value_str, Error& error) +{ + // The new value will be in the m_data. Copy that into our register value. + error = m_reg_value.SetValueFromCString (&m_reg_info, 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, Error &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/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp new file mode 100644 index 00000000000..522ca082a69 --- /dev/null +++ b/source/Core/ValueObjectSyntheticFilter.cpp @@ -0,0 +1,270 @@ +//===-- ValueObjectSyntheticFilter.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Core/ValueObjectSyntheticFilter.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ValueObject.h" +#include "lldb/DataFormatters/FormatClasses.h" + +using namespace lldb_private; + +class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd +{ +public: + DummySyntheticFrontEnd(ValueObject &backend) : + SyntheticChildrenFrontEnd(backend) + {} + + size_t + CalculateNumChildren() + { + return 0; + } + + lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + return lldb::ValueObjectSP(); + } + + size_t + GetIndexOfChildWithName (const ConstString &name) + { + return UINT32_MAX; + } + + bool + MightHaveChildren () + { + return true; + } + + bool + Update() + { + 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_parent_type_name(parent.GetTypeName()), + m_might_have_children(eLazyBoolCalculate) +{ +#ifdef LLDB_CONFIGURATION_DEBUG + std::string new_name(parent.GetName().AsCString()); + new_name += "$$__synth__"; + SetName (ConstString(new_name.c_str())); +#else + SetName(parent.GetName()); +#endif + CopyParentData(); + CreateSynthFilter(); +} + +ValueObjectSynthetic::~ValueObjectSynthetic() +{ +} + +ClangASTType +ValueObjectSynthetic::GetClangTypeImpl () +{ + return m_parent->GetClangType(); +} + +ConstString +ValueObjectSynthetic::GetTypeName() +{ + return m_parent->GetTypeName(); +} + +ConstString +ValueObjectSynthetic::GetQualifiedTypeName() +{ + return m_parent->GetQualifiedTypeName(); +} + +size_t +ValueObjectSynthetic::CalculateNumChildren() +{ + UpdateValueIfNeeded(); + if (m_synthetic_children_count < UINT32_MAX) + return m_synthetic_children_count; + return (m_synthetic_children_count = m_synth_filter_ap->CalculateNumChildren()); +} + +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_ap->MightHaveChildren() ? eLazyBoolYes : eLazyBoolNo); + return (m_might_have_children == eLazyBoolNo ? false : true); +} + +uint64_t +ValueObjectSynthetic::GetByteSize() +{ + return m_parent->GetByteSize(); +} + +lldb::ValueType +ValueObjectSynthetic::GetValueType() const +{ + return m_parent->GetValueType(); +} + +void +ValueObjectSynthetic::CreateSynthFilter () +{ + m_synth_filter_ap = (m_synth_sp->GetFrontEnd(*m_parent)); + if (!m_synth_filter_ap.get()) + m_synth_filter_ap.reset(new DummySyntheticFrontEnd(*m_parent)); +} + +bool +ValueObjectSynthetic::UpdateValue () +{ + 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 + // + ConstString new_parent_type_name = m_parent->GetTypeName(); + if (new_parent_type_name != m_parent_type_name) + { + m_parent_type_name = new_parent_type_name; + CreateSynthFilter(); + } + + // let our backend do its update + if (m_synth_filter_ap->Update() == false) + { + // 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_count = UINT32_MAX; + m_might_have_children = eLazyBoolCalculate; + } + + CopyParentData(); + + SetValueIsValid(true); + return true; +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetChildAtIndex (size_t idx, bool can_create) +{ + UpdateValueIfNeeded(); + + ByIndexIterator iter = m_children_byindex.find(idx); + + if (iter == m_children_byindex.end()) + { + if (can_create && m_synth_filter_ap.get() != NULL) + { + lldb::ValueObjectSP synth_guy = m_synth_filter_ap->GetChildAtIndex (idx); + if (!synth_guy) + return synth_guy; + m_children_byindex[idx]= synth_guy.get(); + return synth_guy; + } + else + return lldb::ValueObjectSP(); + } + else + return iter->second->GetSP(); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetChildMemberWithName (const 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 (const ConstString &name) +{ + UpdateValueIfNeeded(); + + NameToIndexIterator iter = m_name_toindex.find(name.GetCString()); + + if (iter == m_name_toindex.end() && m_synth_filter_ap.get() != NULL) + { + uint32_t index = m_synth_filter_ap->GetIndexOfChildWithName (name); + if (index == UINT32_MAX) + return index; + m_name_toindex[name.GetCString()] = index; + return index; + } + else if (iter == m_name_toindex.end() && m_synth_filter_ap.get() == NULL) + return UINT32_MAX; + else /*if (iter != m_name_toindex.end())*/ + return iter->second; +} + +bool +ValueObjectSynthetic::IsInScope () +{ + return m_parent->IsInScope(); +} + +lldb::ValueObjectSP +ValueObjectSynthetic::GetNonSyntheticValue () +{ + return m_parent->GetSP(); +} + +void +ValueObjectSynthetic::CopyParentData () +{ + m_value = m_parent->GetValue(); + ExecutionContext exe_ctx (GetExecutionContextRef()); + m_error = m_value.GetValueAsData (&exe_ctx, m_data, 0, GetModule().get()); +} diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp new file mode 100644 index 00000000000..38c0d91324a --- /dev/null +++ b/source/Core/ValueObjectVariable.cpp @@ -0,0 +1,386 @@ +//===-- ValueObjectVariable.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Core/ValueObjectVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Core/Value.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/Target/Thread.h" + + +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() != NULL); + m_name = var_sp->GetName(); +} + +ValueObjectVariable::~ValueObjectVariable() +{ +} + +ClangASTType +ValueObjectVariable::GetClangTypeImpl () +{ + Type *var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetClangForwardType(); + return ClangASTType(); +} + +ConstString +ValueObjectVariable::GetTypeName() +{ + Type * var_type = m_variable_sp->GetType(); + if (var_type) + return var_type->GetName(); + 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() +{ + ClangASTType type(GetClangType()); + + if (!type.IsValid()) + return 0; + + const bool omit_empty_base_classes = true; + return type.GetNumChildren(omit_empty_base_classes); +} + +uint64_t +ValueObjectVariable::GetByteSize() +{ + ClangASTType type(GetClangType()); + + if (!type.IsValid()) + return 0; + + return type.GetByteSize(); +} + +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, NULL); + } + 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, NULL, NULL, NULL, loclist_base_load_addr, NULL, m_value, &m_error)) + { + m_resolved_value = m_value; + m_value.SetContext(Value::eContextTypeVariable, variable); + + Value::ValueType value_type = m_value.GetValueType(); + + switch (value_type) + { + case Value::eValueTypeFileAddress: + SetAddressTypeOfChildren(eAddressTypeFile); + break; + case Value::eValueTypeHostAddress: + 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. + Process *process = exe_ctx.GetProcessPtr(); + if (value_type == Value::eValueTypeFileAddress && process && process->IsAlive()) + { + lldb::addr_t file_addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (file_addr != LLDB_INVALID_ADDRESS) + { + SymbolContext var_sc; + variable->CalculateSymbolContext(&var_sc); + if (var_sc.module_sp) + { + ObjectFile *objfile = var_sc.module_sp->GetObjectFile(); + if (objfile) + { + Address so_addr(file_addr, objfile->GetSectionList()); + lldb::addr_t load_addr = so_addr.GetLoadAddress (target); + if (load_addr != LLDB_INVALID_ADDRESS) + { + m_value.SetValueType(Value::eValueTypeLoadAddress); + m_value.GetScalar() = load_addr; + } + } + } + } + } + + if (GetClangType().IsAggregateType()) + { + // 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()); + } + break; + } + + SetValueIsValid (m_error.Success()); + } + else + { + // could not find location, won't allow editing + m_resolved_value.SetContext(Value::eContextTypeInvalid, NULL); + } + } + 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 NULL; +} + +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, Error& error) +{ + 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.SetValueFromCString(reg_info, 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, Error &error) +{ + 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, false); + 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/source/DataFormatters/CF.cpp b/source/DataFormatters/CF.cpp new file mode 100644 index 00000000000..a4b7a1235ff --- /dev/null +++ b/source/DataFormatters/CF.cpp @@ -0,0 +1,299 @@ +//===-- CF.cpp ----------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::CFAbsoluteTimeSummaryProvider (ValueObject& valobj, Stream& stream) +{ + 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) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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 == ConstString("__CFBag") || type_name == ConstString("const struct __CFBag")) + { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok == false) + { + StackFrameSP frame_sp(valobj.GetFrameSP()); + if (!frame_sp) + return false; + ValueObjectSP count_sp; + StreamString expr; + expr.Printf("(int)CFBagGetCount((void*)0x%" PRIx64 ")",valobj.GetPointerValue()); + if (process_sp->GetTarget().EvaluateExpression(expr.GetData(), frame_sp.get(), count_sp) != eExecutionCompleted) + return false; + if (!count_sp) + return false; + count = count_sp->GetValueAsUnsigned(0); + } + else + { + uint32_t offset = 2*ptr_size+4 + valobj_addr; + Error error; + count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error); + if (error.Fail()) + return false; + } + stream.Printf("@\"%u value%s\"", + count,(count == 1 ? "" : "s")); + return true; +} + +bool +lldb_private::formatters::CFBitVectorSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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 == ConstString("__CFMutableBitVector") || type_name == ConstString("__CFBitVector") || type_name == ConstString("CFMutableBitVectorRef") || type_name == ConstString("CFBitVectorRef")) + { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok == false) + return false; + + Error 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 (int 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) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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()); + if (type_name == ConstString("__CFBinaryHeap") || type_name == ConstString("const struct __CFBinaryHeap")) + { + if (valobj.IsPointerType()) + is_type_ok = true; + } + } + + if (is_type_ok == false) + { + StackFrameSP frame_sp(valobj.GetFrameSP()); + if (!frame_sp) + return false; + ValueObjectSP count_sp; + StreamString expr; + expr.Printf("(int)CFBinaryHeapGetCount((void*)0x%" PRIx64 ")",valobj.GetPointerValue()); + if (process_sp->GetTarget().EvaluateExpression(expr.GetData(), frame_sp.get(), count_sp) != eExecutionCompleted) + return false; + if (!count_sp) + return false; + count = count_sp->GetValueAsUnsigned(0); + } + else + { + uint32_t offset = 2*ptr_size; + Error error; + count = process_sp->ReadUnsignedIntegerFromMemory(offset, 4, 0, error); + if (error.Fail()) + return false; + } + stream.Printf("@\"%u item%s\"", + count,(count == 1 ? "" : "s")); + return true; +} diff --git a/source/DataFormatters/CXXFormatterFunctions.cpp b/source/DataFormatters/CXXFormatterFunctions.cpp new file mode 100644 index 00000000000..fba92170d83 --- /dev/null +++ b/source/DataFormatters/CXXFormatterFunctions.cpp @@ -0,0 +1,1351 @@ +//===-- CXXFormatterFunctions.cpp---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "llvm/Support/ConvertUTF.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +#include + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::ExtractValueFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + uint64_t &value) +{ + if (!target_type || !*target_type) + return false; + if (!selector || !*selector) + return false; + StreamString expr; + expr.Printf("(%s)[(id)0x%" PRIx64 " %s]",target_type,valobj.GetPointerValue(),selector); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return false; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + result_sp, + options); + if (!result_sp) + return false; + value = result_sp->GetValueAsUnsigned(0); + return true; +} + +bool +lldb_private::formatters::ExtractSummaryFromObjCExpression (ValueObject &valobj, + const char* target_type, + const char* selector, + Stream &stream) +{ + if (!target_type || !*target_type) + return false; + if (!selector || !*selector) + return false; + StreamString expr; + expr.Printf("(%s)[(id)0x%" PRIx64 " %s]",target_type,valobj.GetPointerValue(),selector); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return false; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + result_sp, + options); + if (!result_sp) + return false; + stream.Printf("%s",result_sp->GetSummaryAsCString()); + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + uint64_t index) +{ + lldb::ValueObjectSP valobj_sp; + if (!return_type || !*return_type) + return valobj_sp; + if (!selector || !*selector) + return valobj_sp; + StreamString expr_path_stream; + valobj.GetExpressionPath(expr_path_stream, false); + StreamString expr; + expr.Printf("(%s)[%s %s:%" PRId64 "]",return_type,expr_path_stream.GetData(),selector,index); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + valobj_sp, + options); + return valobj_sp; +} + +lldb::ValueObjectSP +lldb_private::formatters::CallSelectorOnObject (ValueObject &valobj, + const char* return_type, + const char* selector, + const char* key) +{ + lldb::ValueObjectSP valobj_sp; + if (!return_type || !*return_type) + return valobj_sp; + if (!selector || !*selector) + return valobj_sp; + if (!key || !*key) + return valobj_sp; + StreamString expr_path_stream; + valobj.GetExpressionPath(expr_path_stream, false); + StreamString expr; + expr.Printf("(%s)[%s %s:%s]",return_type,expr_path_stream.GetData(),selector,key); + ExecutionContext exe_ctx (valobj.GetExecutionContextRef()); + lldb::ValueObjectSP result_sp; + Target* target = exe_ctx.GetTargetPtr(); + StackFrame* stack_frame = exe_ctx.GetFramePtr(); + if (!target || !stack_frame) + return valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); + + target->EvaluateExpression(expr.GetData(), + stack_frame, + valobj_sp, + options); + return valobj_sp; +} + +// use this call if you already have an LLDB-side buffer for the data +template +static bool +DumpUTFBufferToStream (ConversionResult (*ConvertFunction) (const SourceDataType**, + const SourceDataType*, + UTF8**, + UTF8*, + ConversionFlags), + DataExtractor& data, + Stream& stream, + char prefix_token = '@', + char quote = '"', + uint32_t sourceSize = 0) +{ + if (prefix_token != 0) + stream.Printf("%c",prefix_token); + if (quote != 0) + stream.Printf("%c",quote); + if (data.GetByteSize() && data.GetDataStart() && data.GetDataEnd()) + { + const int bufferSPSize = data.GetByteSize(); + if (sourceSize == 0) + { + const int origin_encoding = 8*sizeof(SourceDataType); + sourceSize = bufferSPSize/(origin_encoding / 4); + } + + SourceDataType *data_ptr = (SourceDataType*)data.GetDataStart(); + SourceDataType *data_end_ptr = data_ptr + sourceSize; + + while (data_ptr < data_end_ptr) + { + if (!*data_ptr) + { + data_end_ptr = data_ptr; + break; + } + data_ptr++; + } + + data_ptr = (SourceDataType*)data.GetDataStart(); + + lldb::DataBufferSP utf8_data_buffer_sp; + UTF8* utf8_data_ptr = nullptr; + UTF8* utf8_data_end_ptr = nullptr; + + if (ConvertFunction) + { + utf8_data_buffer_sp.reset(new DataBufferHeap(4*bufferSPSize,0)); + utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); + utf8_data_end_ptr = utf8_data_ptr + utf8_data_buffer_sp->GetByteSize(); + ConvertFunction ( (const SourceDataType**)&data_ptr, data_end_ptr, &utf8_data_ptr, utf8_data_end_ptr, lenientConversion ); + utf8_data_ptr = (UTF8*)utf8_data_buffer_sp->GetBytes(); // needed because the ConvertFunction will change the value of the data_ptr + } + 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 = (UTF8*)data_ptr; + utf8_data_end_ptr = (UTF8*)data_end_ptr; + } + + // 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; utf8_data_ptr++) + { + if (!*utf8_data_ptr) + break; + stream.Printf("%c",*utf8_data_ptr); + } + } + if (quote != 0) + stream.Printf("%c",quote); + return true; +} + +template +class ReadUTFBufferAndDumpToStreamOptions +{ +public: + typedef ConversionResult (*ConvertFunctionType) (const SourceDataType**, + const SourceDataType*, + UTF8**, + UTF8*, + ConversionFlags); + + ReadUTFBufferAndDumpToStreamOptions () : + m_conversion_function(NULL), + m_location(0), + m_process_sp(), + m_stream(NULL), + m_prefix_token('@'), + m_quote('"'), + m_source_size(0), + m_needs_zero_termination(true) + { + } + + ReadUTFBufferAndDumpToStreamOptions& + SetConversionFunction (ConvertFunctionType f) + { + m_conversion_function = f; + return *this; + } + + ConvertFunctionType + GetConversionFunction () const + { + return m_conversion_function; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetLocation (uint64_t l) + { + m_location = l; + return *this; + } + + uint64_t + GetLocation () const + { + return m_location; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetProcessSP (ProcessSP p) + { + m_process_sp = p; + return *this; + } + + ProcessSP + GetProcessSP () const + { + return m_process_sp; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetStream (Stream* s) + { + m_stream = s; + return *this; + } + + Stream* + GetStream () const + { + return m_stream; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetPrefixToken (char p) + { + m_prefix_token = p; + return *this; + } + + char + GetPrefixToken () const + { + return m_prefix_token; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetQuote (char q) + { + m_quote = q; + return *this; + } + + char + GetQuote () const + { + return m_quote; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetSourceSize (uint32_t s) + { + m_source_size = s; + return *this; + } + + uint32_t + GetSourceSize () const + { + return m_source_size; + } + + ReadUTFBufferAndDumpToStreamOptions& + SetNeedsZeroTermination (bool z) + { + m_needs_zero_termination = z; + return *this; + } + + bool + GetNeedsZeroTermination () const + { + return m_needs_zero_termination; + } + +private: + ConvertFunctionType m_conversion_function; + uint64_t m_location; + ProcessSP m_process_sp; + Stream* m_stream; + char m_prefix_token; + char m_quote; + uint32_t m_source_size; + bool m_needs_zero_termination; +}; + +template +static bool +ReadUTFBufferAndDumpToStream (const ReadUTFBufferAndDumpToStreamOptions& options) +{ + if (options.GetLocation() == 0 || options.GetLocation() == LLDB_INVALID_ADDRESS) + return false; + + 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 && !options.GetConversionFunction()) + return false; + + if (!options.GetStream()) + return false; + + uint32_t sourceSize = options.GetSourceSize(); + bool needs_zero_terminator = options.GetNeedsZeroTermination(); + + if (!sourceSize) + { + sourceSize = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + needs_zero_terminator = true; + } + else + sourceSize = std::min(sourceSize,process_sp->GetTarget().GetMaximumSizeOfStringSummary()); + + const int bufferSPSize = sourceSize * type_width; + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(bufferSPSize,0)); + + if (!buffer_sp->GetBytes()) + return false; + + Error error; + char *buffer = reinterpret_cast(buffer_sp->GetBytes()); + + size_t data_read = 0; + if (needs_zero_terminator) + data_read = process_sp->ReadStringFromMemory(options.GetLocation(), buffer, bufferSPSize, error, type_width); + else + data_read = process_sp->ReadMemoryFromInferior(options.GetLocation(), (char*)buffer_sp->GetBytes(), bufferSPSize, error); + + if (error.Fail() || data_read == 0) + { + options.GetStream()->Printf("unable to read data"); + return true; + } + + DataExtractor data(buffer_sp, process_sp->GetByteOrder(), process_sp->GetAddressByteSize()); + + return DumpUTFBufferToStream(options.GetConversionFunction(), data, *options.GetStream(), options.GetPrefixToken(), options.GetQuote(), sourceSize); +} + +bool +lldb_private::formatters::Char16StringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(valobj_addr); + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('u'); + + if (!ReadUTFBufferAndDumpToStream(options)) + { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool +lldb_private::formatters::Char32StringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t valobj_addr = valobj.GetValueAsUnsigned(0); + + if (!valobj_addr) + return false; + + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(valobj_addr); + options.SetConversionFunction(ConvertUTF32toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('U'); + + if (!ReadUTFBufferAndDumpToStream(options)) + { + stream.Printf("Summary Unavailable"); + return true; + } + + return true; +} + +bool +lldb_private::formatters::WCharStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + lldb::addr_t data_addr = 0; + + if (valobj.IsPointerType()) + data_addr = valobj.GetValueAsUnsigned(0); + else if (valobj.IsArrayType()) + data_addr = valobj.GetAddressOf(); + + if (data_addr == 0 || data_addr == LLDB_INVALID_ADDRESS) + return false; + + clang::ASTContext* ast = valobj.GetClangType().GetASTContext(); + + if (!ast) + return false; + + ClangASTType wchar_clang_type = ClangASTContext::GetBasicType(ast, lldb::eBasicTypeWChar); + const uint32_t wchar_size = wchar_clang_type.GetBitSize(); + + switch (wchar_size) + { + case 8: + { + // utf 8 + + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(data_addr); + options.SetConversionFunction(nullptr); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + + return ReadUTFBufferAndDumpToStream(options); + } + case 16: + { + // utf 16 + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(data_addr); + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + + return ReadUTFBufferAndDumpToStream(options); + } + case 32: + { + // utf 32 + ReadUTFBufferAndDumpToStreamOptions options; + options.SetLocation(data_addr); + options.SetConversionFunction(ConvertUTF32toUTF8); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('L'); + + return ReadUTFBufferAndDumpToStream(options); + } + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} + +bool +lldb_private::formatters::Char16SummaryProvider (ValueObject& valobj, Stream& stream) +{ + DataExtractor data; + valobj.GetData(data); + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode16, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + return DumpUTFBufferToStream(ConvertUTF16toUTF8,data,stream, 'u','\'',1); +} + +bool +lldb_private::formatters::Char32SummaryProvider (ValueObject& valobj, Stream& stream) +{ + DataExtractor data; + valobj.GetData(data); + + std::string value; + valobj.GetValueAsCString(lldb::eFormatUnicode32, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + + return DumpUTFBufferToStream(ConvertUTF32toUTF8,data,stream, 'U','\'',1); +} + +bool +lldb_private::formatters::WCharSummaryProvider (ValueObject& valobj, Stream& stream) +{ + DataExtractor data; + valobj.GetData(data); + + clang::ASTContext* ast = valobj.GetClangType().GetASTContext(); + + if (!ast) + return false; + + ClangASTType wchar_clang_type = ClangASTContext::GetBasicType(ast, lldb::eBasicTypeWChar); + const uint32_t wchar_size = wchar_clang_type.GetBitSize(); + std::string value; + + switch (wchar_size) + { + case 8: + // utf 8 + valobj.GetValueAsCString(lldb::eFormatChar, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + return DumpUTFBufferToStream(nullptr, + data, + stream, + 'L', + '\'', + 1); + case 16: + // utf 16 + valobj.GetValueAsCString(lldb::eFormatUnicode16, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + return DumpUTFBufferToStream(ConvertUTF16toUTF8, + data, + stream, + 'L', + '\'', + 1); + case 32: + // utf 32 + valobj.GetValueAsCString(lldb::eFormatUnicode32, value); + if (!value.empty()) + stream.Printf("%s ", value.c_str()); + return DumpUTFBufferToStream(ConvertUTF32toUTF8, + data, + stream, + 'L', + '\'', + 1); + default: + stream.Printf("size for wchar_t is not valid"); + return true; + } + return true; +} + +// 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({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) +{ + uint64_t size = 0; + ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) + { + stream.Printf("L\"\""); + return true; + } + if (!location_sp) + return false; + return WCharStringSummaryProvider(*location_sp.get(), stream); +} + +bool +lldb_private::formatters::LibcxxStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + uint64_t size = 0; + ValueObjectSP location_sp((ValueObject*)nullptr); + if (!ExtractLibcxxStringInfo(valobj, location_sp, size)) + return false; + if (size == 0) + { + stream.Printf("\"\""); + return true; + } + if (!location_sp) + return false; + Error error; + if (location_sp->ReadPointedString(stream, + error, + 0, // max length is decided by the settings + false) == 0) // do not honor array (terminates on first 0 byte even for a char[]) + stream.Printf("\"\""); // if nothing was read, print an empty string + return error.Success(); +} + +bool +lldb_private::formatters::ObjCClassSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptorFromISA(valobj.GetValueAsUnsigned(0))); + + if (!descriptor.get() || !descriptor->IsValid()) + return false; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + stream.Printf("%s",class_name); + return true; +} + +class ObjCClassSyntheticChildrenFrontEnd : public SyntheticChildrenFrontEnd +{ +public: + ObjCClassSyntheticChildrenFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()) + { + } + + virtual size_t + CalculateNumChildren () + { + return 0; + } + + virtual lldb::ValueObjectSP + GetChildAtIndex (size_t idx) + { + return lldb::ValueObjectSP(); + } + + virtual bool + Update() + { + return false; + } + + virtual bool + MightHaveChildren () + { + return false; + } + + virtual size_t + GetIndexOfChildWithName (const ConstString &name) + { + return UINT32_MAX; + } + + virtual + ~ObjCClassSyntheticChildrenFrontEnd () + { + } +}; + +SyntheticChildrenFrontEnd* +lldb_private::formatters::ObjCClassSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + return new ObjCClassSyntheticChildrenFrontEnd(valobj_sp); +} + +template +bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSConcreteData") || + !strcmp(class_name,"NSConcreteMutableData") || + !strcmp(class_name,"__NSCFData")) + { + uint32_t offset = (is_64bit ? 16 : 8); + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + offset, is_64bit ? 8 : 4, 0, error); + if (error.Fail()) + return false; + } + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "length", value)) + return false; + } + + stream.Printf("%s%" PRIu64 " byte%s%s", + (needs_at ? "@\"" : ""), + value, + (value != 1 ? "s" : ""), + (needs_at ? "\"" : "")); + + return true; +} + +static bool +ReadAsciiBufferAndDumpToStream (lldb::addr_t location, + lldb::ProcessSP& process_sp, + Stream& dest, + uint32_t size = 0, + Error* error = NULL, + size_t *data_read = NULL, + char prefix_token = '@', + char quote = '"') +{ + Error my_error; + size_t my_data_read; + if (!process_sp || location == 0) + return false; + + if (!size) + size = process_sp->GetTarget().GetMaximumSizeOfStringSummary(); + else + size = std::min(size,process_sp->GetTarget().GetMaximumSizeOfStringSummary()); + + lldb::DataBufferSP buffer_sp(new DataBufferHeap(size,0)); + + my_data_read = process_sp->ReadCStringFromMemory(location, (char*)buffer_sp->GetBytes(), size, my_error); + + if (error) + *error = my_error; + if (data_read) + *data_read = my_data_read; + + if (my_error.Fail()) + return false; + + dest.Printf("%c%c",prefix_token,quote); + + if (my_data_read) + dest.Printf("%s",(char*)buffer_sp->GetBytes()); + + dest.Printf("%c",quote); + + return true; +} + +bool +lldb_private::formatters::NSStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + uint64_t info_bits_location = valobj_addr + ptr_size; + if (process_sp->GetByteOrder() != lldb::eByteOrderLittle) + info_bits_location += 3; + + Error 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_special = strcmp(class_name,"NSPathStore2") == 0; + bool has_null = (info_bits & 8) == 8; + + size_t explicit_length = 0; + if (!has_null && has_explicit_length && !is_special) + { + lldb::addr_t explicit_length_offset = 2*ptr_size; + if (is_mutable and not 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 (not is_inline and not 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; + } + + 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 and is_unicode) + { + ReadUTFBufferAndDumpToStreamOptions options; + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(false); + return ReadUTFBufferAndDumpToStream (options); + } + else + return ReadAsciiBufferAndDumpToStream(location+1,process_sp,stream, explicit_length); + } + else if (is_inline && has_explicit_length && !is_unicode && !is_special && !is_mutable) + { + uint64_t location = 3 * ptr_size + valobj_addr; + return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + } + else if (is_unicode) + { + uint64_t location = valobj_addr + 2*ptr_size; + if (is_inline) + { + if (!has_explicit_length) + { + stream.Printf("found new combo"); + return true; + } + else + location += ptr_size; + } + else + { + location = process_sp->ReadPointerFromMemory(location, error); + if (error.Fail()) + return false; + } + ReadUTFBufferAndDumpToStreamOptions options; + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(has_explicit_length == false); + return ReadUTFBufferAndDumpToStream (options); + } + else if (is_special) + { + uint64_t location = valobj_addr + (ptr_size == 8 ? 12 : 8); + ReadUTFBufferAndDumpToStreamOptions options; + options.SetConversionFunction(ConvertUTF16toUTF8); + options.SetLocation(location); + options.SetProcessSP(process_sp); + options.SetStream(&stream); + options.SetPrefixToken('@'); + options.SetQuote('"'); + options.SetSourceSize(explicit_length); + options.SetNeedsZeroTermination(has_explicit_length == false); + return ReadUTFBufferAndDumpToStream (options); + } + else if (is_inline) + { + uint64_t location = valobj_addr + 2*ptr_size; + if (!has_explicit_length) + location++; + return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + } + 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 + return ReadAsciiBufferAndDumpToStream(location,process_sp,stream,explicit_length); + } + + stream.Printf("class name = %s",class_name); + return true; + +} + +bool +lldb_private::formatters::NSAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + 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; + ClangASTType type(valobj.GetClangType()); + 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; + child_ptr_sp->GetData(data); + 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); + return false; +} + +bool +lldb_private::formatters::NSMutableAttributedStringSummaryProvider (ValueObject& valobj, Stream& stream) +{ + return NSAttributedStringSummaryProvider(valobj, stream); +} + +bool +lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider (ValueObject& valobj, Stream& stream) +{ + stream.Printf("%s",valobj.GetObjectDescription()); + return true; +} + +bool +lldb_private::formatters::ObjCBOOLSummaryProvider (ValueObject& valobj, Stream& stream) +{ + const uint32_t type_info = valobj.GetClangType().GetTypeInfo(); + + ValueObjectSP real_guy_sp = valobj.GetSP(); + + if (type_info & ClangASTType::eTypeIsPointer) + { + Error err; + real_guy_sp = valobj.Dereference(err); + if (err.Fail() || !real_guy_sp) + return false; + } + else if (type_info & ClangASTType::eTypeIsReference) + { + real_guy_sp = valobj.GetChildAtIndex(0, true); + if (!real_guy_sp) + return false; + } + uint64_t value = real_guy_sp->GetValueAsUnsigned(0); + if (value == 0) + { + stream.Printf("NO"); + return true; + } + stream.Printf("YES"); + return true; +} + +template +bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject& valobj, Stream& stream) +{ + lldb::ValueObjectSP valobj_sp; + + ClangASTType charstar (valobj.GetClangType().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; + valobj.GetData(data); + 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) + { + 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; // for some reason, we need to subtract 1900 from this field. not sure why. + tm_epoch.tm_isdst = -1; + tm_epoch.tm_gmtoff = 0; + tm_epoch.tm_zone = NULL; + epoch = timegm(&tm_epoch); + } + return epoch; +} + +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 = NULL; + 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_private::formatters::VectorIteratorSyntheticFrontEnd::VectorIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp, + ConstString item_name) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_item_name(item_name), +m_item_sp() +{ + if (valobj_sp) + Update(); +} + +bool +lldb_private::formatters::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; + Error err; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + m_item_sp = ValueObject::CreateValueObjectFromAddress("item", item_ptr->GetValueAsUnsigned(0), m_exe_ctx_ref, item_ptr->GetClangType().GetPointeeType()); + if (err.Fail()) + m_item_sp.reset(); + return false; +} + +size_t +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::CalculateNumChildren () +{ + return 1; +} + +lldb::ValueObjectSP +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx == 0) + return m_item_sp; + return lldb::ValueObjectSP(); +} + +bool +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("item")) + return 0; + return UINT32_MAX; +} + +lldb_private::formatters::VectorIteratorSyntheticFrontEnd::~VectorIteratorSyntheticFrontEnd () +{ +} + +template bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::NSDataSummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::ObjCSELSummaryProvider (ValueObject&, Stream&) ; diff --git a/source/DataFormatters/Cocoa.cpp b/source/DataFormatters/Cocoa.cpp new file mode 100644 index 00000000000..555954db0bb --- /dev/null +++ b/source/DataFormatters/Cocoa.cpp @@ -0,0 +1,565 @@ +//===-- Cocoa.cpp -------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::NSBundleSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSBundle")) + { + uint64_t offset = 5 * ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType().GetBasicTypeFromAST(lldb::eBasicTypeObjCID), true)); + + StreamString summary_stream; + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + if (was_nsstring_ok && summary_stream.GetSize() > 0) + { + stream.Printf("%s",summary_stream.GetData()); + return true; + } + } + // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] + // which is encoded differently and needs to be handled by running code + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "bundlePath", stream); +} + +bool +lldb_private::formatters::NSTimeZoneSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSTimeZone")) + { + uint64_t offset = ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true)); + StreamString summary_stream; + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + if (was_nsstring_ok && summary_stream.GetSize() > 0) + { + stream.Printf("%s",summary_stream.GetData()); + return true; + } + } + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream); +} + +bool +lldb_private::formatters::NSNotificationSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSConcreteNotification")) + { + uint64_t offset = ptr_size; + ValueObjectSP text(valobj.GetSyntheticChildAtOffset(offset, valobj.GetClangType(), true)); + StreamString summary_stream; + bool was_nsstring_ok = NSStringSummaryProvider(*text.get(), summary_stream); + if (was_nsstring_ok && summary_stream.GetSize() > 0) + { + stream.Printf("%s",summary_stream.GetData()); + return true; + } + } + // this is either an unknown subclass or an NSBundle that comes from [NSBundle mainBundle] + // which is encoded differently and needs to be handled by running code + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "name", stream); +} + +bool +lldb_private::formatters::NSMachPortSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + uint64_t port_number = 0; + + do + { + if (!strcmp(class_name,"NSMachPort")) + { + uint64_t offset = (ptr_size == 4 ? 12 : 20); + Error error; + port_number = process_sp->ReadUnsignedIntegerFromMemory(offset+valobj_addr, 4, 0, error); + if (error.Success()) + break; + } + if (!ExtractValueFromObjCExpression(valobj, "int", "machPort", port_number)) + return false; + } while (false); + + stream.Printf("mach port: %u",(uint32_t)(port_number & 0x00000000FFFFFFFF)); + return true; +} + +bool +lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + uint64_t count = 0; + + do { + if (!strcmp(class_name,"NSIndexSet") || !strcmp(class_name,"NSMutableIndexSet")) + { + Error 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 + { + if (!ExtractValueFromObjCExpression(valobj, "unsigned long long int", "count", count)) + return false; + } + } while (false); + stream.Printf("%" PRIu64 " index%s", + count, + (count == 1 ? "" : "es")); + return true; +} + +bool +lldb_private::formatters::NSNumberSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"NSNumber") || !strcmp(class_name,"__NSCFNumber")) + { + uint64_t value = 0; + uint64_t i_bits = 0; + if (descriptor->GetTaggedPointerInfo(&i_bits,&value)) + { + switch (i_bits) + { + case 0: + stream.Printf("(char)%hhd",(char)value); + break; + case 1: + case 4: + stream.Printf("(short)%hd",(short)value); + break; + case 2: + case 8: + stream.Printf("(int)%d",(int)value); + break; + case 3: + case 12: + stream.Printf("(long)%" PRId64,value); + break; + default: + stream.Printf("unexpected value:(info=%" PRIu64 ", value=%" PRIu64,i_bits,value); + break; + } + return true; + } + else + { + Error error; + uint8_t data_type = (process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, 1, 0, error) & 0x1F); + uint64_t data_location = valobj_addr + 2*ptr_size; + uint64_t value = 0; + if (error.Fail()) + return false; + switch (data_type) + { + case 1: // 0B00001 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 1, 0, error); + if (error.Fail()) + return false; + stream.Printf("(char)%hhd",(char)value); + break; + case 2: // 0B0010 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 2, 0, error); + if (error.Fail()) + return false; + stream.Printf("(short)%hd",(short)value); + break; + case 3: // 0B0011 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); + if (error.Fail()) + return false; + stream.Printf("(int)%d",(int)value); + break; + case 17: // 0B10001 + data_location += 8; + case 4: // 0B0100 + value = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); + if (error.Fail()) + return false; + stream.Printf("(long)%" PRId64,value); + break; + case 5: // 0B0101 + { + uint32_t flt_as_int = process_sp->ReadUnsignedIntegerFromMemory(data_location, 4, 0, error); + if (error.Fail()) + return false; + float flt_value = *((float*)&flt_as_int); + stream.Printf("(float)%f",flt_value); + break; + } + case 6: // 0B0110 + { + uint64_t dbl_as_lng = process_sp->ReadUnsignedIntegerFromMemory(data_location, 8, 0, error); + if (error.Fail()) + return false; + double dbl_value = *((double*)&dbl_as_lng); + stream.Printf("(double)%g",dbl_value); + break; + } + default: + stream.Printf("unexpected value: dt=%d",data_type); + break; + } + return true; + } + } + else + { + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "stringValue", stream); + } +} + +bool +lldb_private::formatters::NSURLSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (strcmp(class_name, "NSURL") == 0) + { + 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; + ClangASTType type(valobj.GetClangType()); + 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)) + return false; + if (base && base->GetValueAsUnsigned(0)) + { + if (summary.GetSize() > 0) + summary.GetString().resize(summary.GetSize()-1); + summary.Printf(" -- "); + StreamString base_summary; + if (NSURLSummaryProvider(*base, base_summary) && base_summary.GetSize() > 0) + summary.Printf("%s",base_summary.GetSize() > 2 ? base_summary.GetData() + 2 : base_summary.GetData()); + } + if (summary.GetSize()) + { + stream.Printf("%s",summary.GetData()); + return true; + } + } + else + { + return ExtractSummaryFromObjCExpression(valobj, "NSString*", "description", stream); + } + return false; +} + +bool +lldb_private::formatters::NSDateSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + uint64_t date_value_bits = 0; + double date_value = 0.0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (strcmp(class_name,"NSDate") == 0 || + strcmp(class_name,"__NSDate") == 0 || + strcmp(class_name,"__NSTaggedDate") == 0) + { + uint64_t info_bits=0,value_bits = 0; + if (descriptor->GetTaggedPointerInfo(&info_bits,&value_bits)) + { + date_value_bits = ((value_bits << 8) | (info_bits << 4)); + date_value = *((double*)&date_value_bits); + } + else + { + Error error; + date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+ptr_size, 8, 0, error); + date_value = *((double*)&date_value_bits); + if (error.Fail()) + return false; + } + } + else if (!strcmp(class_name,"NSCalendarDate")) + { + Error error; + date_value_bits = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr+2*ptr_size, 8, 0, error); + date_value = *((double*)&date_value_bits); + if (error.Fail()) + return false; + } + else + { + if (ExtractValueFromObjCExpression(valobj, "NSTimeInterval", "ExtractValueFromObjCExpression", date_value_bits) == false) + return false; + date_value = *((double*)&date_value_bits); + } + if (date_value == -63114076800) + { + stream.Printf("0001-12-30 00:00:00 +0000"); + return true; + } + // 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 = 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; +} diff --git a/source/DataFormatters/DataVisualization.cpp b/source/DataFormatters/DataVisualization.cpp new file mode 100644 index 00000000000..c1ef359049b --- /dev/null +++ b/source/DataFormatters/DataVisualization.cpp @@ -0,0 +1,279 @@ +//===-- DataVisualization.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Debugger.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(); +} + +lldb::TypeFormatImplSP +DataVisualization::ValueFormats::GetFormat (ValueObject& valobj, lldb::DynamicValueType use_dynamic) +{ + lldb::TypeFormatImplSP entry; + GetFormatManager().GetValueNavigator().Get(valobj, entry, use_dynamic); + return entry; +} + +lldb::TypeFormatImplSP +DataVisualization::ValueFormats::GetFormat (const ConstString &type) +{ + lldb::TypeFormatImplSP entry; + GetFormatManager().GetValueNavigator().Get(type, entry); + return entry; +} + +void +DataVisualization::ValueFormats::Add (const ConstString &type, const lldb::TypeFormatImplSP &entry) +{ + GetFormatManager().GetValueNavigator().Add(FormatManager::GetValidTypeName(type),entry); +} + +bool +DataVisualization::ValueFormats::Delete (const ConstString &type) +{ + return GetFormatManager().GetValueNavigator().Delete(type); +} + +void +DataVisualization::ValueFormats::Clear () +{ + GetFormatManager().GetValueNavigator().Clear(); +} + +void +DataVisualization::ValueFormats::LoopThrough (TypeFormatImpl::ValueCallback callback, void* callback_baton) +{ + GetFormatManager().GetValueNavigator().LoopThrough(callback, callback_baton); +} + +size_t +DataVisualization::ValueFormats::GetCount () +{ + return GetFormatManager().GetValueNavigator().GetCount(); +} + +lldb::TypeNameSpecifierImplSP +DataVisualization::ValueFormats::GetTypeNameSpecifierForFormatAtIndex (size_t index) +{ + return GetFormatManager().GetValueNavigator().GetTypeNameSpecifierAtIndex(index); +} + +lldb::TypeFormatImplSP +DataVisualization::ValueFormats::GetFormatAtIndex (size_t index) +{ + return GetFormatManager().GetValueNavigator().GetAtIndex(index); +} + +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); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +DataVisualization::GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + return GetFormatManager().GetSyntheticChildren(valobj, use_dynamic); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +DataVisualization::GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetSyntheticChildrenForType(type_sp); +} +#endif + +lldb::TypeFilterImplSP +DataVisualization::GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetFilterForType(type_sp); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::ScriptedSyntheticChildrenSP +DataVisualization::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + return GetFormatManager().GetSyntheticForType(type_sp); +} +#endif + +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 (const ConstString &category, lldb::TypeCategoryImplSP &entry, + bool allow_create) +{ + entry = GetFormatManager().GetCategory(category, allow_create); + return (entry.get() != NULL); +} + +void +DataVisualization::Categories::Add (const ConstString &category) +{ + GetFormatManager().GetCategory(category); +} + +bool +DataVisualization::Categories::Delete (const ConstString &category) +{ + GetFormatManager().DisableCategory(category); + return GetFormatManager().DeleteCategory(category); +} + +void +DataVisualization::Categories::Clear () +{ + GetFormatManager().ClearCategories(); +} + +void +DataVisualization::Categories::Clear (const ConstString &category) +{ + GetFormatManager().GetCategory(category)->Clear(eFormatCategoryItemSummary | eFormatCategoryItemRegexSummary); +} + +void +DataVisualization::Categories::Enable (const ConstString& category, + TypeCategoryMap::Position pos) +{ + if (GetFormatManager().GetCategory(category)->IsEnabled()) + GetFormatManager().DisableCategory(category); + GetFormatManager().EnableCategory(category, pos); +} + +void +DataVisualization::Categories::Disable (const ConstString& category) +{ + if (GetFormatManager().GetCategory(category)->IsEnabled() == true) + GetFormatManager().DisableCategory(category); +} + +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() == true) + GetFormatManager().DisableCategory(category); +} + +void +DataVisualization::Categories::LoopThrough (FormatManager::CategoryCallback callback, void* callback_baton) +{ + GetFormatManager().LoopThroughCategories(callback, callback_baton); +} + +uint32_t +DataVisualization::Categories::GetCount () +{ + return GetFormatManager().GetCategoriesCount(); +} + +lldb::TypeCategoryImplSP +DataVisualization::Categories::GetCategoryAtIndex (size_t index) +{ + return GetFormatManager().GetCategoryAtIndex(index); +} + +bool +DataVisualization::NamedSummaryFormats::GetSummaryFormat (const ConstString &type, lldb::TypeSummaryImplSP &entry) +{ + return GetFormatManager().GetNamedSummaryNavigator().Get(type,entry); +} + +void +DataVisualization::NamedSummaryFormats::Add (const ConstString &type, const lldb::TypeSummaryImplSP &entry) +{ + GetFormatManager().GetNamedSummaryNavigator().Add(FormatManager::GetValidTypeName(type),entry); +} + +bool +DataVisualization::NamedSummaryFormats::Delete (const ConstString &type) +{ + return GetFormatManager().GetNamedSummaryNavigator().Delete(type); +} + +void +DataVisualization::NamedSummaryFormats::Clear () +{ + GetFormatManager().GetNamedSummaryNavigator().Clear(); +} + +void +DataVisualization::NamedSummaryFormats::LoopThrough (TypeSummaryImpl::SummaryCallback callback, void* callback_baton) +{ + GetFormatManager().GetNamedSummaryNavigator().LoopThrough(callback, callback_baton); +} + +uint32_t +DataVisualization::NamedSummaryFormats::GetCount () +{ + return GetFormatManager().GetNamedSummaryNavigator().GetCount(); +} diff --git a/source/DataFormatters/FormatCache.cpp b/source/DataFormatters/FormatCache.cpp new file mode 100644 index 00000000000..af7b1c386c3 --- /dev/null +++ b/source/DataFormatters/FormatCache.cpp @@ -0,0 +1,169 @@ +//===-- FormatCache.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/DataFormatters/FormatCache.h" + +using namespace lldb; +using namespace lldb_private; + +FormatCache::Entry::Entry () : +m_summary_cached(false), +m_synthetic_cached(false), +m_summary_sp(), +m_synthetic_sp() +{} + +FormatCache::Entry::Entry (lldb::TypeSummaryImplSP summary_sp) : +m_synthetic_cached(false), +m_synthetic_sp() +{ + SetSummary (summary_sp); +} + +FormatCache::Entry::Entry (lldb::SyntheticChildrenSP synthetic_sp) : +m_summary_cached(false), +m_summary_sp() +{ + SetSynthetic (synthetic_sp); +} + +FormatCache::Entry::Entry (lldb::TypeSummaryImplSP summary_sp,lldb::SyntheticChildrenSP synthetic_sp) +{ + SetSummary (summary_sp); + SetSynthetic (synthetic_sp); +} + +bool +FormatCache::Entry::IsSummaryCached () +{ + return m_summary_cached; +} + +bool +FormatCache::Entry::IsSyntheticCached () +{ + return m_synthetic_cached; +} + +lldb::TypeSummaryImplSP +FormatCache::Entry::GetSummary () +{ + return m_summary_sp; +} + +lldb::SyntheticChildrenSP +FormatCache::Entry::GetSynthetic () +{ + return m_synthetic_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; +} + +FormatCache::FormatCache () : +m_map(), +m_mutex (Mutex::eMutexTypeRecursive) +#ifdef LLDB_CONFIGURATION_DEBUG +,m_cache_hits(0),m_cache_misses(0) +#endif +{ +} + +FormatCache::Entry& +FormatCache::GetEntry (const 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::GetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp) +{ + Mutex::Locker lock(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 (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp) +{ + Mutex::Locker lock(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; +} + +void +FormatCache::SetSummary (const ConstString& type,lldb::TypeSummaryImplSP& summary_sp) +{ + Mutex::Locker lock(m_mutex); + GetEntry(type).SetSummary(summary_sp); +} + +void +FormatCache::SetSynthetic (const ConstString& type,lldb::SyntheticChildrenSP& synthetic_sp) +{ + Mutex::Locker lock(m_mutex); + GetEntry(type).SetSynthetic(synthetic_sp); +} + +void +FormatCache::Clear () +{ + Mutex::Locker lock(m_mutex); + m_map.clear(); +} + diff --git a/source/DataFormatters/FormatClasses.cpp b/source/DataFormatters/FormatClasses.cpp new file mode 100644 index 00000000000..c67f86a7493 --- /dev/null +++ b/source/DataFormatters/FormatClasses.cpp @@ -0,0 +1,33 @@ +//===-- FormatClasses.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/DataFormatters/FormatClasses.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + diff --git a/source/DataFormatters/FormatManager.cpp b/source/DataFormatters/FormatManager.cpp new file mode 100644 index 00000000000..eeae8bc9a19 --- /dev/null +++ b/source/DataFormatters/FormatManager.cpp @@ -0,0 +1,1083 @@ +//===-- FormatManager.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/FormatManager.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Debugger.h" +#include "lldb/DataFormatters/CXXFormatterFunctions.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Platform.h" + +using namespace lldb; +using namespace lldb_private; + + +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[]" }, + { 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 = sizeof(g_format_infos)/sizeof(FormatInfo); + +static bool +GetFormatFromFormatChar (char format_char, Format &format) +{ + for (uint32_t i=0; i= eFormatDefault && format < kNumFormats) + return g_format_infos[format].format_name; + return NULL; +} + +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() == false) + continue; + lldb::TypeSummaryImplSP summary_current_sp = category_sp->GetSummaryForType(type_sp); + if (summary_current_sp && (summary_chosen_sp.get() == NULL || (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() == false) + continue; + lldb::TypeFilterImplSP filter_current_sp((TypeFilterImpl*)category_sp->GetFilterForType(type_sp).get()); + if (filter_current_sp && (filter_chosen_sp.get() == NULL || (prio_category > category_sp->GetEnabledPosition()))) + { + prio_category = category_sp->GetEnabledPosition(); + filter_chosen_sp = filter_current_sp; + } + } + return filter_chosen_sp; +} + +#ifndef LLDB_DISABLE_PYTHON +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() == false) + continue; + lldb::ScriptedSyntheticChildrenSP synth_current_sp((ScriptedSyntheticChildren*)category_sp->GetSyntheticForType(type_sp).get()); + if (synth_current_sp && (synth_chosen_sp.get() == NULL || (prio_category > category_sp->GetEnabledPosition()))) + { + prio_category = category_sp->GetEnabledPosition(); + synth_chosen_sp = synth_current_sp; + } + } + return synth_chosen_sp; +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +FormatManager::GetSyntheticChildrenForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + if (!type_sp) + return lldb::SyntheticChildrenSP(); + lldb::TypeFilterImplSP filter_sp = GetFilterForType(type_sp); + lldb::ScriptedSyntheticChildrenSP synth_sp = GetSyntheticForType(type_sp); + if (filter_sp->GetRevision() > synth_sp->GetRevision()) + return lldb::SyntheticChildrenSP(filter_sp.get()); + else + return lldb::SyntheticChildrenSP(synth_sp.get()); +} +#endif + +lldb::TypeCategoryImplSP +FormatManager::GetCategory (const 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 eFormatVectorOfFloat32: + case eFormatVectorOfFloat64: + return eFormatFloat; + + default: + return lldb::eFormatInvalid; + } +} + +ConstString +FormatManager::GetValidTypeName (const ConstString& type) +{ + return ::GetValidTypeName_Impl(type); +} + +ConstString +GetTypeForCache (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + if (use_dynamic == lldb::eNoDynamicValues) + { + if (valobj.IsDynamic()) + { + if (valobj.GetStaticValue()) + return valobj.GetStaticValue()->GetQualifiedTypeName(); + else + return ConstString(); + } + else + return valobj.GetQualifiedTypeName(); + } + if (valobj.IsDynamic()) + return valobj.GetQualifiedTypeName(); + if (valobj.GetDynamicValue(use_dynamic)) + return valobj.GetDynamicValue(use_dynamic)->GetQualifiedTypeName(); + return ConstString(); +} + +lldb::TypeSummaryImplSP +FormatManager::GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + TypeSummaryImplSP retval; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + ConstString valobj_type(GetTypeForCache(valobj, use_dynamic)); + if (valobj_type) + { + if (log) + log->Printf("\n\n[FormatManager::GetSummaryFormat] Looking into cache for type %s", valobj_type.AsCString("")); + if (m_format_cache.GetSummary(valobj_type,retval)) + { + if (log) + { + log->Printf("[FormatManager::GetSummaryFormat] Cache search success. Returning."); + if (log->GetDebug()) + log->Printf("[FormatManager::GetSummaryFormat] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, 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(valobj, use_dynamic); + if (valobj_type) + { + if (log) + log->Printf("[FormatManager::GetSummaryFormat] Caching %p for type %s",retval.get(),valobj_type.AsCString("")); + m_format_cache.SetSummary(valobj_type,retval); + } + if (log && log->GetDebug()) + log->Printf("[FormatManager::GetSummaryFormat] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + return retval; +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +FormatManager::GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + SyntheticChildrenSP retval; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + ConstString valobj_type(GetTypeForCache(valobj, use_dynamic)); + if (valobj_type) + { + if (log) + log->Printf("\n\n[FormatManager::GetSyntheticChildren] Looking into cache for type %s", valobj_type.AsCString("")); + if (m_format_cache.GetSynthetic(valobj_type,retval)) + { + if (log) + { + log->Printf("[FormatManager::GetSyntheticChildren] Cache search success. Returning."); + if (log->GetDebug()) + log->Printf("[FormatManager::GetSyntheticChildren] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, 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(valobj, use_dynamic); + if (valobj_type) + { + if (log) + log->Printf("[FormatManager::GetSyntheticChildren] Caching %p for type %s",retval.get(),valobj_type.AsCString("")); + m_format_cache.SetSynthetic(valobj_type,retval); + } + if (log && log->GetDebug()) + log->Printf("[FormatManager::GetSyntheticChildren] Cache hits: %" PRIu64 " - Cache Misses: %" PRIu64, m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses()); + return retval; +} +#endif + +FormatManager::FormatManager() : + m_format_cache(), + m_value_nav("format",this), + m_named_summaries_map(this), + m_last_revision(0), + m_categories_map(this), + m_default_category_name(ConstString("default")), + m_system_category_name(ConstString("system")), + m_gnu_cpp_category_name(ConstString("gnu-libstdc++")), + m_libcxx_category_name(ConstString("libcxx")), + m_objc_category_name(ConstString("objc")), + m_corefoundation_category_name(ConstString("CoreFoundation")), + m_coregraphics_category_name(ConstString("CoreGraphics")), + m_coreservices_category_name(ConstString("CoreServices")), + m_vectortypes_category_name(ConstString("VectorTypes")), + m_appkit_category_name(ConstString("AppKit")) +{ + LoadSystemFormatters(); + LoadLibStdcppFormatters(); + LoadLibcxxFormatters(); + LoadObjCFormatters(); + + EnableCategory(m_objc_category_name,TypeCategoryMap::Last); + EnableCategory(m_corefoundation_category_name,TypeCategoryMap::Last); + EnableCategory(m_appkit_category_name,TypeCategoryMap::Last); + EnableCategory(m_coreservices_category_name,TypeCategoryMap::Last); + EnableCategory(m_coregraphics_category_name,TypeCategoryMap::Last); + EnableCategory(m_gnu_cpp_category_name,TypeCategoryMap::Last); + EnableCategory(m_libcxx_category_name,TypeCategoryMap::Last); + EnableCategory(m_vectortypes_category_name,TypeCategoryMap::Last); + EnableCategory(m_system_category_name,TypeCategoryMap::Last); +} + +static void +AddStringSummary(TypeCategoryImpl::SharedPointer category_sp, + const char* string, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + lldb::TypeSummaryImplSP summary_sp(new StringSummaryFormat(flags, + string)); + + if (regex) + category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); +} + +#ifndef LLDB_DISABLE_PYTHON +static void +AddScriptSummary(TypeCategoryImpl::SharedPointer category_sp, + const char* funct_name, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + + std::string code(" "); + code.append(funct_name).append("(valobj,internal_dict)"); + + lldb::TypeSummaryImplSP summary_sp(new ScriptSummaryFormat(flags, + funct_name, + code.c_str())); + if (regex) + category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +static void +AddCXXSummary (TypeCategoryImpl::SharedPointer category_sp, + CXXFunctionSummaryFormat::Callback funct, + const char* description, + ConstString type_name, + TypeSummaryImpl::Flags flags, + bool regex = false) +{ + lldb::TypeSummaryImplSP summary_sp(new CXXFunctionSummaryFormat(flags,funct,description)); + if (regex) + category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),summary_sp); + else + category_sp->GetSummaryNavigator()->Add(type_name, summary_sp); +} +#endif + +#ifndef LLDB_DISABLE_PYTHON +static void AddCXXSynthetic (TypeCategoryImpl::SharedPointer category_sp, + CXXSyntheticChildren::CreateFrontEndCallback generator, + const char* description, + ConstString type_name, + ScriptedSyntheticChildren::Flags flags, + bool regex = false) +{ + lldb::SyntheticChildrenSP synth_sp(new CXXSyntheticChildren(flags,description,generator)); + if (regex) + category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())), synth_sp); + else + category_sp->GetSyntheticNavigator()->Add(type_name,synth_sp); +} +#endif + +void +FormatManager::LoadLibStdcppFormatters() +{ + 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}")); + + TypeCategoryImpl::SharedPointer gnu_category_sp = GetCategory(m_gnu_cpp_category_name); + + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::string"), + std_string_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string"), + std_string_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string,std::allocator >"), + std_string_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string, std::allocator >"), + std_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}")); + + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::wstring"), + std_wstring_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string"), + std_wstring_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string,std::allocator >"), + std_wstring_summary_sp); + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::basic_string, std::allocator >"), + std_wstring_summary_sp); + + +#ifndef LLDB_DISABLE_PYTHON + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); + + gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdVectorSynthProvider"))); + gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdMapSynthProvider"))); + gnu_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider"))); + + stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(true); + gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::vector<.+>(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); + gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::map<.+> >(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); + gnu_category_sp->GetRegexSummaryNavigator()->Add(RegularExpressionSP(new RegularExpression("^std::list<.+>(( )?&)?$")), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, + "size=${svar%#}"))); + + AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibStdcppVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^__gnu_cxx::__normal_iterator<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic(gnu_category_sp, lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::_Rb_tree_iterator<.+>$"), stl_synth_flags, true); + + gnu_category_sp->GetSummaryNavigator()->Add(ConstString("std::vector >"), + TypeSummaryImplSP(new StringSummaryFormat(stl_summary_flags, "size=${svar%#}"))); + + gnu_category_sp->GetSyntheticNavigator()->Add(ConstString("std::vector >"), + SyntheticChildrenSP(new CXXSyntheticChildren(stl_synth_flags,"libc++ std::vector synthetic children",lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator))); + +#endif +} + +void +FormatManager::LoadLibcxxFormatters() +{ + TypeSummaryImpl::Flags stl_summary_flags; + stl_summary_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + +#ifndef LLDB_DISABLE_PYTHON + //std::string code(" lldb.formatters.cpp.libcxx.stdstring_SummaryProvider(valobj,internal_dict)"); + //lldb::TypeSummaryImplSP std_string_summary_sp(new ScriptSummaryFormat(stl_summary_flags, "lldb.formatters.cpp.libcxx.stdstring_SummaryProvider",code.c_str())); + + lldb::TypeSummaryImplSP std_string_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, lldb_private::formatters::LibcxxStringSummaryProvider, "std::string summary provider")); + lldb::TypeSummaryImplSP std_wstring_summary_sp(new CXXFunctionSummaryFormat(stl_summary_flags, lldb_private::formatters::LibcxxWStringSummaryProvider, "std::wstring summary provider")); + + TypeCategoryImpl::SharedPointer libcxx_category_sp = GetCategory(m_libcxx_category_name); + + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::string"), + std_string_summary_sp); + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string, std::__1::allocator >"), + std_string_summary_sp); + + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::wstring"), + std_wstring_summary_sp); + libcxx_category_sp->GetSummaryNavigator()->Add(ConstString("std::__1::basic_string, std::__1::allocator >"), + std_wstring_summary_sp); + + SyntheticChildren::Flags stl_synth_flags; + stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(false); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator, "libc++ std::vector synthetic children", ConstString("^std::__1::vector<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator, "libc++ std::list synthetic children", ConstString("^std::__1::list<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::map synthetic children", ConstString("^std::__1::map<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator, "libc++ std::vector synthetic children", ConstString("std::__1::vector >"), stl_synth_flags); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::set synthetic children", ConstString("^std::__1::set<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multiset synthetic children", ConstString("^std::__1::multiset<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multimap synthetic children", ConstString("^std::__1::multimap<.+> >(( )?&)?$"), stl_synth_flags, true); + + libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")), + SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, + "lldb.formatters.cpp.libcxx.stddeque_SynthProvider"))); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, "shared_ptr synthetic children", ConstString("^(std::__1::)shared_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator, "weak_ptr synthetic children", ConstString("^(std::__1::)weak_ptr<.+>(( )?&)?$"), stl_synth_flags, true); + + stl_summary_flags.SetDontShowChildren(false);stl_summary_flags.SetSkipPointers(false); + + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::vector summary provider", ConstString("^std::__1::vector<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::list summary provider", ConstString("^std::__1::list<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::map summary provider", ConstString("^std::__1::map<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::deque summary provider", ConstString("^std::__1::deque<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::vector summary provider", ConstString("std::__1::vector >"), stl_summary_flags); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::set summary provider", ConstString("^std::__1::set<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::multiset summary provider", ConstString("^std::__1::multiset<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::multimap summary provider", ConstString("^std::__1::multimap<.+>(( )?&)?$"), stl_summary_flags, true); + + stl_summary_flags.SetSkipPointers(true); + AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, true); + AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::weak_ptr<.+>(( )?&)?$"), stl_summary_flags, true); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibCxxVectorIteratorSyntheticFrontEndCreator, "std::vector iterator synthetic children", ConstString("^std::__1::__wrap_iter<.+>$"), stl_synth_flags, true); + + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEndCreator, "std::map iterator synthetic children", ConstString("^std::__1::__map_iterator<.+>$"), stl_synth_flags, true); + +#endif +} + +void +FormatManager::LoadSystemFormatters() +{ + + TypeSummaryImpl::Flags string_flags; + string_flags.SetCascades(true) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + lldb::TypeSummaryImplSP string_format(new StringSummaryFormat(string_flags, "${var%s}")); + + + lldb::TypeSummaryImplSP string_array_format(new StringSummaryFormat(TypeSummaryImpl::Flags().SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false), + "${var%s}")); + + lldb::RegularExpressionSP any_size_char_arr(new RegularExpression("char \\[[0-9]+\\]")); + + TypeCategoryImpl::SharedPointer sys_category_sp = GetCategory(m_system_category_name); + + sys_category_sp->GetSummaryNavigator()->Add(ConstString("char *"), string_format); + sys_category_sp->GetSummaryNavigator()->Add(ConstString("unsigned char *"), string_format); + sys_category_sp->GetRegexSummaryNavigator()->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->GetSummaryNavigator()->Add(ConstString("OSType"), ostype_summary); + +#ifndef LLDB_DISABLE_PYTHON + // FIXME because of a bug in the FormatNavigator we need to add a summary for both X* and const X* () + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16StringSummaryProvider, "char16_t * summary provider", ConstString("char16_t *"), string_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char32StringSummaryProvider, "char32_t * summary provider", ConstString("char32_t *"), string_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::WCharStringSummaryProvider, "wchar_t * summary provider", ConstString("wchar_t *"), string_flags); + + AddCXXSummary(sys_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(sys_category_sp, lldb_private::formatters::Char16SummaryProvider, "char16_t summary provider", ConstString("char16_t"), widechar_flags); + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char32SummaryProvider, "char32_t summary provider", ConstString("char32_t"), widechar_flags); + AddCXXSummary(sys_category_sp, lldb_private::formatters::WCharSummaryProvider, "wchar_t summary provider", ConstString("wchar_t"), widechar_flags); + + AddCXXSummary(sys_category_sp, lldb_private::formatters::Char16SummaryProvider, "unichar summary provider", ConstString("unichar"), widechar_flags); + +#endif +} + +void +FormatManager::LoadObjCFormatters() +{ + TypeSummaryImpl::Flags objc_flags; + objc_flags.SetCascades(false) + .SetSkipPointers(true) + .SetSkipReferences(true) + .SetDontShowChildren(true) + .SetDontShowValue(true) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + TypeCategoryImpl::SharedPointer objc_category_sp = GetCategory(m_objc_category_name); + + lldb::TypeSummaryImplSP ObjC_BOOL_summary(new CXXFunctionSummaryFormat(objc_flags, lldb_private::formatters::ObjCBOOLSummaryProvider,"")); + objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL"), + ObjC_BOOL_summary); + objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL &"), + ObjC_BOOL_summary); + objc_category_sp->GetSummaryNavigator()->Add(ConstString("BOOL *"), + ObjC_BOOL_summary); + +#ifndef LLDB_DISABLE_PYTHON + // 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, "SEL summary provider", ConstString("SEL"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("struct objc_selector"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("objc_selector"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "SEL summary provider", ConstString("objc_selector *"), objc_flags); + AddCXXSummary(objc_category_sp, lldb_private::formatters::ObjCSELSummaryProvider, "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); +#endif // LLDB_DISABLE_PYTHON + + 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); + + TypeCategoryImpl::SharedPointer corefoundation_category_sp = GetCategory(m_corefoundation_category_name); + + AddStringSummary(corefoundation_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(corefoundation_category_sp, + "location=${var.location} length=${var.length}", + ConstString("CFRange"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "(x=${var.x}, y=${var.y})", + ConstString("NSPoint"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "location=${var.location}, length=${var.length}", + ConstString("NSRange"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "${var.origin}, ${var.size}", + ConstString("NSRect"), + objc_flags); + AddStringSummary(corefoundation_category_sp, + "(${var.origin}, ${var.size}), ...", + ConstString("NSRectArray"), + objc_flags); + AddStringSummary(objc_category_sp, + "(width=${var.width}, height=${var.height})", + ConstString("NSSize"), + objc_flags); + + TypeCategoryImpl::SharedPointer coregraphics_category_sp = GetCategory(m_coregraphics_category_name); + + AddStringSummary(coregraphics_category_sp, + "(width=${var.width}, height=${var.height})", + ConstString("CGSize"), + objc_flags); + AddStringSummary(coregraphics_category_sp, + "(x=${var.x}, y=${var.y})", + ConstString("CGPoint"), + objc_flags); + AddStringSummary(coregraphics_category_sp, + "origin=${var.origin} size=${var.size}", + ConstString("CGRect"), + objc_flags); + + TypeCategoryImpl::SharedPointer coreservices_category_sp = GetCategory(m_coreservices_category_name); + + AddStringSummary(coreservices_category_sp, + "red=${var.red} green=${var.green} blue=${var.blue}", + ConstString("RGBColor"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "(t=${var.top}, l=${var.left}, b=${var.bottom}, r=${var.right})", + ConstString("Rect"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "(v=${var.v}, h=${var.h})", + ConstString("Point"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "${var.month}/${var.day}/${var.year} ${var.hour} :${var.minute} :${var.second} dayOfWeek:${var.dayOfWeek}", + ConstString("DateTimeRect *"), + objc_flags); + AddStringSummary(coreservices_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(coreservices_category_sp, + "(x=${var.x}, y=${var.y})", + ConstString("HIPoint"), + objc_flags); + AddStringSummary(coreservices_category_sp, + "origin=${var.origin} size=${var.size}", + ConstString("HIRect"), + objc_flags); + + TypeCategoryImpl::SharedPointer appkit_category_sp = GetCategory(m_appkit_category_name); + + TypeSummaryImpl::Flags appkit_flags; + appkit_flags.SetCascades(true) + .SetSkipPointers(false) + .SetSkipReferences(false) + .SetDontShowChildren(true) + .SetDontShowValue(false) + .SetShowMembersOneLiner(false) + .SetHideItemNames(false); + + appkit_flags.SetDontShowChildren(false); + + +#ifndef LLDB_DISABLE_PYTHON + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("NSArray"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("NSMutableArray"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArrayI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSArrayM"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("__NSCFArray"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("CFArrayRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSArraySummaryProvider, "NSArray summary provider", ConstString("CFMutableArrayRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("NSDictionary"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("NSMutableDictionary"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSCFDictionary"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSDictionaryI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("__NSDictionaryM"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("CFDictionaryRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDictionarySummaryProvider, "NSDictionary summary provider", ConstString("CFMutableDictionaryRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSSet summary", ConstString("NSSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "CFSetRef summary", ConstString("CFSetRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "CFMutableSetRef summary", ConstString("CFMutableSetRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSCFSet summary", ConstString("__NSCFSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSSetI summary", ConstString("__NSSetI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSSetM summary", ConstString("__NSSetM"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSCountedSet summary", ConstString("NSCountedSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSMutableSet summary", ConstString("NSMutableSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "NSOrderedSet summary", ConstString("NSOrderedSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSOrderedSetI summary", ConstString("__NSOrderedSetI"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSSetSummaryProvider, "__NSOrderedSetM summary", ConstString("__NSOrderedSetM"), appkit_flags); + + // AddSummary(appkit_category_sp, "${var.key%@} -> ${var.value%@}", ConstString("$_lldb_typegen_nspair"), appkit_flags); + + appkit_flags.SetDontShowChildren(true); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArrayM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSArrayI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("NSArray"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("NSMutableArray"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("__NSCFArray"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("CFMutableArrayRef"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSArraySyntheticFrontEndCreator, "NSArray synthetic children", ConstString("CFArrayRef"), ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSDictionaryM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("__NSDictionaryI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("NSDictionary"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("NSMutableDictionary"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFDictionaryRef"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSDictionarySyntheticFrontEndCreator, "NSDictionary synthetic children", ConstString("CFMutableDictionaryRef"), ScriptedSyntheticChildren::Flags()); + + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSSet synthetic children", ConstString("NSSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetI synthetic children", ConstString("__NSSetI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSSetM synthetic children", ConstString("__NSSetM"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSMutableSet synthetic children", ConstString("NSMutableSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "NSOrderedSet synthetic children", ConstString("NSOrderedSet"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetI synthetic children", ConstString("__NSOrderedSetI"), ScriptedSyntheticChildren::Flags()); + AddCXXSynthetic(appkit_category_sp, lldb_private::formatters::NSSetSyntheticFrontEndCreator, "__NSOrderedSetM synthetic children", ConstString("__NSOrderedSetM"), ScriptedSyntheticChildren::Flags()); + + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFBagRef"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("__CFBag"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("const struct __CFBag"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBagSummaryProvider, "CFBag summary provider", ConstString("CFMutableBagRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBinaryHeapSummaryProvider, "CFBinaryHeap summary provider", ConstString("CFBinaryHeapRef"), appkit_flags); + AddCXXSummary(appkit_category_sp,lldb_private::formatters::CFBinaryHeapSummaryProvider, "CFBinaryHeap summary provider", ConstString("__CFBinaryHeap"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFStringRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("CFMutableStringRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSMutableString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFConstantString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("__NSCFString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSCFConstantString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSCFString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSStringSummaryProvider, "NSString summary provider", ConstString("NSPathStore2"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSAttributedStringSummaryProvider, "NSAttributedString summary provider", ConstString("NSAttributedString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSMutableAttributedStringSummaryProvider, "NSMutableAttributedString summary provider", ConstString("NSMutableAttributedString"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSMutableAttributedStringSummaryProvider, "NSMutableAttributedString summary provider", ConstString("NSConcreteMutableAttributedString"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSBundleSummaryProvider, "NSBundle summary provider", ConstString("NSBundle"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSConcreteData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("NSConcreteMutableData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("__NSCFData"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("CFDataRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDataSummaryProvider, "NSData summary provider", ConstString("CFMutableDataRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSMachPortSummaryProvider, "NSMachPort summary provider", ConstString("NSMachPort"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, "NSNotification summary provider", ConstString("NSNotification"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNotificationSummaryProvider, "NSNotification summary provider", ConstString("NSConcreteNotification"), appkit_flags); + + AddStringSummary(appkit_category_sp, "domain: ${var._domain} - code: ${var._code}", ConstString("NSError"), appkit_flags); + AddStringSummary(appkit_category_sp,"name:${var.name%S} reason:${var.reason%S}",ConstString("NSException"),appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSNumber"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFBoolean"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("__NSCFNumber"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFBoolean"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSNumberSummaryProvider, "NSNumber summary provider", ConstString("NSCFNumber"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSDecimalNumber summary provider", ConstString("NSDecimalNumber"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSHost summary provider", ConstString("NSHost"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSTask summary provider", ConstString("NSTask"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::RuntimeSpecificDescriptionSummaryProvider, "NSValue summary provider", ConstString("NSValue"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("NSURL"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSURLSummaryProvider, "NSURL summary provider", ConstString("CFURLRef"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSDate"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("__NSDate"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("__NSTaggedDate"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSDateSummaryProvider, "NSDate summary provider", ConstString("NSCalendarDate"), appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("NSTimeZone"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSTimeZoneSummaryProvider, "NSTimeZone summary provider", ConstString("CFTimeZoneRef"), appkit_flags); + AddCXXSummary(appkit_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(appkit_category_sp, lldb_private::formatters::CFAbsoluteTimeSummaryProvider, "CFAbsoluteTime summary provider", ConstString("CFAbsoluteTime"), appkit_flags); + appkit_flags.SetDontShowValue(false); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, "NSIndexSet summary provider", ConstString("NSIndexSet"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::NSIndexSetSummaryProvider, "NSIndexSet summary provider", ConstString("NSMutableIndexSet"), appkit_flags); + + AddStringSummary(appkit_category_sp, + "@\"${var.month%d}/${var.day%d}/${var.year%d} ${var.hour%d}:${var.minute%d}:${var.second}\"", + ConstString("CFGregorianDate"), + appkit_flags); + + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("CFBitVectorRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("CFMutableBitVectorRef"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("__CFBitVector"), appkit_flags); + AddCXXSummary(appkit_category_sp, lldb_private::formatters::CFBitVectorSummaryProvider, "CFBitVector summary provider", ConstString("__CFMutableBitVector"), appkit_flags); +#endif // LLDB_DISABLE_PYTHON + + 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"), + objc_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/source/DataFormatters/LibCxx.cpp b/source/DataFormatters/LibCxx.cpp new file mode 100644 index 00000000000..cdc57f6bd93 --- /dev/null +++ b/source/DataFormatters/LibCxx.cpp @@ -0,0 +1,519 @@ +//===-- LibCxx.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::LibcxxVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_count(0), +m_base_data_address(0), +m_options() +{ + if (valobj_sp) + Update(); + m_options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); +} + +size_t +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::CalculateNumChildren () +{ + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= m_count) + return ValueObjectSP(); + if (m_base_data_address == 0 || m_count == 0) + return ValueObjectSP(); + 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 ValueObjectSP(); + uint8_t byte = 0; + uint8_t mask = 0; + Error err; + size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err); + if (err.Fail() || bytes_read == 0) + return ValueObjectSP(); + switch (bit_index) + { + case 0: + mask = 1; break; + case 1: + mask = 2; break; + case 2: + mask = 4; break; + case 3: + mask = 8; break; + case 4: + mask = 16; break; + case 5: + mask = 32; break; + case 6: + mask = 64; break; + case 7: + mask = 128; break; + default: + return ValueObjectSP(); + } + bool bit_set = ((byte & mask) != 0); + Target& target(process_sp->GetTarget()); + ValueObjectSP retval_sp; + if (bit_set) + target.EvaluateExpression("(bool)true", NULL, retval_sp); + else + target.EvaluateExpression("(bool)false", NULL, retval_sp); + StreamString name; name.Printf("[%zu]",idx); + if (retval_sp) + retval_sp->SetName(ConstString(name.GetData())); + return retval_sp; +} + +/*(std::__1::vector >) vBool = { + __begin_ = 0x00000001001000e0 + __size_ = 56 + __cap_alloc_ = { + std::__1::__libcpp_compressed_pair_imp > = { + __first_ = 1 + } + } + }*/ + +bool +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::Update() +{ + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + 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; +} + +bool +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEnd::GetIndexOfChildWithName (const 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::formatters::LibcxxVectorBoolSyntheticFrontEnd::~LibcxxVectorBoolSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxVectorBoolSyntheticFrontEnd(valobj_sp)); +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 + (std::__1::__map_iterator, std::__1::allocator > >, std::__1::__tree_node, std::__1::allocator > >, void *> *, long> >) ibeg = { + __i_ = { + __ptr_ = 0x0000000100103870 { + std::__1::__tree_node_base = { + std::__1::__tree_end_node *> = { + __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.get()), +m_pair_ptr() +{ + if (valobj_sp) + Update(); +} + +bool +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::Update() +{ + 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; + + // 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_", + NULL, + NULL, + NULL, + ValueObject::GetValueForExpressionPathOptions().DontCheckDotVsArrowSyntax().DontAllowSyntheticChildren(), + NULL).get(); + + 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 lldb::ValueObjectSP(); + return m_pair_ptr->GetChildAtIndex(idx, true); +} + +bool +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibCxxMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("first")) + return 0; + if (name == ConstString("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) +{ + if (!valobj_sp) + return NULL; + return (new LibCxxMapIteratorSyntheticFrontEnd(valobj_sp)); +} + +/* + (lldb) fr var ibeg --raw --ptr-depth 1 -T + (std::__1::__wrap_iter) ibeg = { + (std::__1::__wrap_iter::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"); + if (!valobj_sp) + return NULL; + return (new VectorIteratorSyntheticFrontEnd(valobj_sp,g_item_name)); +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::LibcxxSharedPtrSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_cntrl(NULL), +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 = ValueObject::CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_owners_sp->GetClangType()); + } + 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 = ValueObject::CreateValueObjectFromData("count", data, valobj_sp->GetExecutionContextRef(), shared_weak_owners_sp->GetClangType()); + } + return m_weak_count_sp; + } +} + +bool +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::Update() +{ + m_count_sp.reset(); + m_weak_count_sp.reset(); + m_cntrl = NULL; + + 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 (const ConstString &name) +{ + if (name == ConstString("__ptr_")) + return 0; + if (name == ConstString("count")) + return 1; + if (name == ConstString("weak_count")) + return 2; + return UINT32_MAX; +} + +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEnd::~LibcxxSharedPtrSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxSharedPtrSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxSharedPtrSyntheticFrontEnd(valobj_sp)); +} + +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::LibcxxStdVectorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_start(NULL), + m_finish(NULL), + m_element_type(), + m_element_size(0), + m_children() +{ + if (valobj_sp) + Update(); +} + +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(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + uint64_t offset = idx * m_element_size; + offset = offset + m_start->GetValueAsUnsigned(0); + StreamString name; + name.Printf("[%zu]",idx); + ValueObjectSP child_sp = ValueObject::CreateValueObjectFromAddress(name.GetData(), offset, m_backend.GetExecutionContextRef(), m_element_type); + m_children[idx] = child_sp; + return child_sp; +} + +bool +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEnd::Update() +{ + m_start = m_finish = NULL; + m_children.clear(); + ValueObjectSP data_type_finder_sp(m_backend.GetChildMemberWithName(ConstString("__end_cap_"),true)); + if (!data_type_finder_sp) + return false; + data_type_finder_sp = data_type_finder_sp->GetChildMemberWithName(ConstString("__first_"),true); + if (!data_type_finder_sp) + return false; + m_element_type = data_type_finder_sp->GetClangType().GetPointeeType(); + m_element_size = m_element_type.GetByteSize(); + + 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 (const ConstString &name) +{ + if (!m_start || !m_finish) + return UINT32_MAX; + return ExtractIndexFromString(name.GetCString()); +} + +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; +} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdVectorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdVectorSyntheticFrontEnd(valobj_sp)); +} + +bool +lldb_private::formatters::LibcxxContainerSummaryProvider (ValueObject& valobj, Stream& stream) +{ + if (valobj.IsPointerType()) + { + uint64_t value = valobj.GetValueAsUnsigned(0); + if (!value) + return false; + stream.Printf("0x%016" PRIx64 " ", value); + } + return Debugger::FormatPrompt("size=${svar%#}", NULL, NULL, NULL, stream, &valobj); +} diff --git a/source/DataFormatters/LibCxxList.cpp b/source/DataFormatters/LibCxxList.cpp new file mode 100644 index 00000000000..de2ca1b2045 --- /dev/null +++ b/source/DataFormatters/LibCxxList.cpp @@ -0,0 +1,310 @@ +//===-- LibCxxList.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +class ListEntry +{ +public: + ListEntry () {} + ListEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + ListEntry (const ListEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} + ListEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP + next () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__next_"), true); + } + + ValueObjectSP + prev () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__prev_"), true); + } + + uint64_t + value () + { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool + null() + { + return (value() == 0); + } + + ValueObjectSP + GetEntry () + { + return m_entry_sp; + } + + void + SetEntry (ValueObjectSP entry) + { + m_entry_sp = entry; + } + + bool + operator == (const ListEntry& rhs) const + { + return (rhs.m_entry_sp.get() == m_entry_sp.get()); + } + +private: + ValueObjectSP m_entry_sp; +}; + +class ListIterator +{ +public: + ListIterator () {} + ListIterator (ListEntry entry) : m_entry(entry) {} + ListIterator (ValueObjectSP entry) : m_entry(entry) {} + ListIterator (const ListIterator& rhs) : m_entry(rhs.m_entry) {} + 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.SetEntry(m_entry.next()); + } + + void + prev () + { + m_entry.SetEntry(m_entry.prev()); + } +private: + ListEntry m_entry; +}; + +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::LibcxxStdListSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_list_capping_size(0), +m_node_address(), +m_head(NULL), +m_tail(NULL), +m_element_type(), +m_count(UINT32_MAX), +m_children() +{ + if (valobj_sp) + Update(); +} + +bool +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::HasLoop() +{ + if (g_use_loop_detect == false) + return false; + ListEntry slow(m_head); + ListEntry fast1(m_head); + ListEntry fast2(m_head); + while (slow.next() && slow.next()->GetValueAsUnsigned(0) != m_node_address) + { + auto slow_value = slow.value(); + fast1.SetEntry(fast2.next()); + fast2.SetEntry(fast1.next()); + if (fast1.value() == slow_value || fast2.value() == slow_value) + return true; + slow.SetEntry(slow.next()); + } + return false; +} + +size_t +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::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 first(size_alloc->GetChildMemberWithName(ConstString("__first_"), true)); + if (first) + { + m_count = first->GetValueAsUnsigned(UINT32_MAX); + } + } + if (m_count != UINT32_MAX) + { + if (!HasLoop()) + return m_count; + return m_count = 0; + } + 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; + if (HasLoop()) + return 0; + uint64_t size = 2; + ListEntry current(m_head); + while (current.next() && current.next()->GetValueAsUnsigned(0) != m_node_address) + { + size++; + current.SetEntry(current.next()); + if (size > m_list_capping_size) + break; + } + return m_count = (size-1); + } +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + + if (!m_head || !m_tail || m_node_address == 0) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + ListIterator current(m_head); + ValueObjectSP current_sp(current.advance(idx)); + if (!current_sp) + return lldb::ValueObjectSP(); + current_sp = current_sp->GetChildMemberWithName(ConstString("__value_"), true); + 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; + current_sp->GetData(data); + StreamString name; + name.Printf("[%zu]",idx); + return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); +} + +bool +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::Update() +{ + m_head = m_tail = NULL; + m_node_address = 0; + m_count = UINT32_MAX; + Error err; + ValueObjectSP backend_addr(m_backend.AddressOf(err)); + m_list_capping_size = 0; + if (m_backend.GetTargetSP()) + m_list_capping_size = m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); + if (m_list_capping_size == 0) + m_list_capping_size = 255; + if (err.Fail() || backend_addr.get() == NULL) + 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; + ClangASTType list_type = m_backend.GetClangType(); + if (list_type.IsReferenceType()) + list_type = list_type.GetNonReferenceType(); + + if (list_type.GetNumTemplateArguments() == 0) + return false; + lldb::TemplateArgumentKind kind; + m_element_type = list_type.GetTemplateArgument(0, kind); + m_head = impl_sp->GetChildMemberWithName(ConstString("__next_"), true).get(); + m_tail = impl_sp->GetChildMemberWithName(ConstString("__prev_"), true).get(); + return false; +} + +bool +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxStdListSyntheticFrontEnd::~LibcxxStdListSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdListSyntheticFrontEnd(valobj_sp)); +} + diff --git a/source/DataFormatters/LibCxxMap.cpp b/source/DataFormatters/LibCxxMap.cpp new file mode 100644 index 00000000000..5daa40f15f3 --- /dev/null +++ b/source/DataFormatters/LibCxxMap.cpp @@ -0,0 +1,409 @@ +//===-- LibCxxList.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +class MapEntry +{ +public: + MapEntry () {} + MapEntry (ValueObjectSP entry_sp) : m_entry_sp(entry_sp) {} + MapEntry (const MapEntry& rhs) : m_entry_sp(rhs.m_entry_sp) {} + MapEntry (ValueObject* entry) : m_entry_sp(entry ? entry->GetSP() : ValueObjectSP()) {} + + ValueObjectSP + left () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__left_"), true); + } + + ValueObjectSP + right () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__right_"), true); + } + + ValueObjectSP + parent () + { + if (!m_entry_sp) + return m_entry_sp; + return m_entry_sp->GetChildMemberWithName(ConstString("__parent_"), true); + } + + uint64_t + value () + { + if (!m_entry_sp) + return 0; + return m_entry_sp->GetValueAsUnsigned(0); + } + + bool + error () + { + if (!m_entry_sp) + return true; + return m_entry_sp->GetError().Fail(); + } + + bool + null() + { + return (value() == 0); + } + + ValueObjectSP + GetEntry () + { + 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 () {} + 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) + { + if (m_error) + return lldb::ValueObjectSP(); + if (count == 0) + return m_entry.GetEntry(); + if (count == 1) + { + next (); + return m_entry.GetEntry(); + } + size_t steps = 0; + while (count > 0) + { + if (m_error) + return lldb::ValueObjectSP(); + next (); + count--; + if (m_entry.null()) + return lldb::ValueObjectSP(); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return m_entry.GetEntry(); + } +protected: + void + next () + { + m_entry.SetEntry(increment(m_entry.GetEntry())); + } + +private: + ValueObjectSP + tree_min (ValueObjectSP x_sp) + { + MapEntry x(x_sp); + if (x.null()) + return ValueObjectSP(); + MapEntry left(x.left()); + size_t steps = 0; + while (left.null() == false) + { + if (left.error()) + { + m_error = true; + return lldb::ValueObjectSP(); + } + x.SetEntry(left.GetEntry()); + left.SetEntry(x.left()); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return x.GetEntry(); + } + + ValueObjectSP + tree_max (ValueObjectSP x_sp) + { + MapEntry x(x_sp); + if (x.null()) + return ValueObjectSP(); + MapEntry right(x.right()); + size_t steps = 0; + while (right.null() == false) + { + if (right.error()) + return lldb::ValueObjectSP(); + x.SetEntry(right.GetEntry()); + right.SetEntry(x.right()); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return x.GetEntry(); + } + + bool + is_left_child (ValueObjectSP x_sp) + { + MapEntry x(x_sp); + if (x.null()) + return false; + MapEntry rhs(x.parent()); + rhs.SetEntry(rhs.left()); + return x.value() == rhs.value(); + } + + ValueObjectSP + increment (ValueObjectSP x_sp) + { + MapEntry node(x_sp); + if (node.null()) + return ValueObjectSP(); + MapEntry right(node.right()); + if (right.null() == false) + return tree_min(right.GetEntry()); + size_t steps = 0; + while (!is_left_child(node.GetEntry())) + { + if (node.error()) + { + m_error = true; + return lldb::ValueObjectSP(); + } + node.SetEntry(node.parent()); + steps++; + if (steps > m_max_depth) + return lldb::ValueObjectSP(); + } + return node.parent(); + } + + MapEntry m_entry; + size_t m_max_depth; + bool m_error; +}; + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::LibcxxStdMapSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_tree(NULL), +m_root_node(NULL), +m_element_type(), +m_skip_size(UINT32_MAX), +m_count(UINT32_MAX), +m_children() +{ + if (valobj_sp) + Update(); +} + +size_t +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_count != UINT32_MAX) + return m_count; + if (m_tree == NULL) + return 0; + ValueObjectSP m_item(m_tree->GetChildMemberWithName(ConstString("__pair3_"), true)); + if (!m_item) + return 0; + m_item = m_item->GetChildMemberWithName(ConstString("__first_"), true); + if (!m_item) + return 0; + m_count = m_item->GetValueAsUnsigned(0); + return m_count; +} + +bool +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetDataType() +{ + if (m_element_type.GetOpaqueQualType() && m_element_type.GetASTContext()) + return true; + m_element_type.Clear(); + ValueObjectSP deref; + Error error; + deref = m_root_node->Dereference(error); + if (!deref || error.Fail()) + return false; + deref = deref->GetChildMemberWithName(ConstString("__value_"), true); + if (!deref) + return false; + m_element_type = deref->GetClangType(); + return true; +} + +void +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetValueOffset (const lldb::ValueObjectSP& node) +{ + if (m_skip_size != UINT32_MAX) + return; + if (!node) + return; + ClangASTType node_type(node->GetClangType()); + uint64_t bit_offset; + if (node_type.GetIndexOfFieldWithName("__value_", NULL, &bit_offset) == UINT32_MAX) + return; + m_skip_size = bit_offset / 8u; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + if (m_tree == NULL || m_root_node == NULL) + return lldb::ValueObjectSP(); + + auto cached = m_children.find(idx); + if (cached != m_children.end()) + return cached->second; + + bool need_to_skip = (idx > 0); + MapIterator iterator(m_root_node, CalculateNumChildren()); + ValueObjectSP iterated_sp(iterator.advance(idx)); + if (iterated_sp.get() == NULL) + { + // this tree is garbage - stop + m_tree = NULL; // this will stop all future searches until an Update() happens + return iterated_sp; + } + if (GetDataType()) + { + if (!need_to_skip) + { + Error error; + iterated_sp = iterated_sp->Dereference(error); + if (!iterated_sp || error.Fail()) + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + GetValueOffset(iterated_sp); + iterated_sp = iterated_sp->GetChildMemberWithName(ConstString("__value_"), true); + if (!iterated_sp) + { + m_tree = NULL; + 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 = NULL; + return lldb::ValueObjectSP(); + } + iterated_sp = iterated_sp->GetSyntheticChildAtOffset(m_skip_size, m_element_type, true); + if (!iterated_sp) + { + m_tree = NULL; + return lldb::ValueObjectSP(); + } + } + } + else + { + m_tree = NULL; + 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; + iterated_sp->GetData(data); + StreamString name; + name.Printf("[%zu]",idx); + return (m_children[idx] = ValueObject::CreateValueObjectFromData(name.GetData(), data, m_backend.GetExecutionContextRef(), m_element_type)); +} + +bool +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() +{ + m_count = UINT32_MAX; + m_tree = m_root_node = NULL; + m_children.clear(); + m_tree = m_backend.GetChildMemberWithName(ConstString("__tree_"), true).get(); + if (!m_tree) + return false; + m_root_node = m_tree->GetChildMemberWithName(ConstString("__begin_node_"), true).get(); + return false; +} + +bool +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return ExtractIndexFromString(name.GetCString()); +} + +lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::~LibcxxStdMapSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibcxxStdMapSyntheticFrontEnd(valobj_sp)); +} diff --git a/source/DataFormatters/LibStdcpp.cpp b/source/DataFormatters/LibStdcpp.cpp new file mode 100644 index 00000000000..e0f23cc35e3 --- /dev/null +++ b/source/DataFormatters/LibStdcpp.cpp @@ -0,0 +1,331 @@ +//===-- LibStdcpp.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::LibstdcppVectorBoolSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_count(0), +m_base_data_address(0), +m_options() +{ + if (valobj_sp) + Update(); + m_options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); +} + +size_t +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::CalculateNumChildren () +{ + return m_count; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= m_count) + return ValueObjectSP(); + if (m_base_data_address == 0 || m_count == 0) + return ValueObjectSP(); + 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 ValueObjectSP(); + uint8_t byte = 0; + uint8_t mask = 0; + Error err; + size_t bytes_read = process_sp->ReadMemory(byte_location, &byte, 1, err); + if (err.Fail() || bytes_read == 0) + return ValueObjectSP(); + switch (bit_index) + { + case 0: + mask = 1; break; + case 1: + mask = 2; break; + case 2: + mask = 4; break; + case 3: + mask = 8; break; + case 4: + mask = 16; break; + case 5: + mask = 32; break; + case 6: + mask = 64; break; + case 7: + mask = 128; break; + default: + return ValueObjectSP(); + } + bool bit_set = ((byte & mask) != 0); + Target& target(process_sp->GetTarget()); + ValueObjectSP retval_sp; + if (bit_set) + target.EvaluateExpression("(bool)true", NULL, retval_sp); + else + target.EvaluateExpression("(bool)false", NULL, retval_sp); + StreamString name; name.Printf("[%zu]",idx); + if (retval_sp) + retval_sp->SetName(ConstString(name.GetData())); + return retval_sp; +} + +/*((std::vector >) vBool = { + (std::_Bvector_base >) std::_Bvector_base > = { + (std::_Bvector_base >::_Bvector_impl) _M_impl = { + (std::_Bit_iterator) _M_start = { + (std::_Bit_iterator_base) std::_Bit_iterator_base = { + (_Bit_type *) _M_p = 0x0016b160 + (unsigned int) _M_offset = 0 + } + } + (std::_Bit_iterator) _M_finish = { + (std::_Bit_iterator_base) std::_Bit_iterator_base = { + (_Bit_type *) _M_p = 0x0016b16c + (unsigned int) _M_offset = 16 + } + } + (_Bit_type *) _M_end_of_storage = 0x0016b170 + } + } + } + */ + +bool +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::Update() +{ + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + + ValueObjectSP m_impl_sp(valobj_sp->GetChildMemberWithName(ConstString("_M_impl"), true)); + if (!m_impl_sp) + return false; + + ValueObjectSP m_start_sp(m_impl_sp->GetChildMemberWithName(ConstString("_M_start"), true)); + ValueObjectSP m_finish_sp(m_impl_sp->GetChildMemberWithName(ConstString("_M_finish"), true)); + + ValueObjectSP start_p_sp, finish_p_sp, finish_offset_sp; + + if (!m_start_sp || !m_finish_sp) + return false; + + start_p_sp = m_start_sp->GetChildMemberWithName(ConstString("_M_p"), true); + finish_p_sp = m_finish_sp->GetChildMemberWithName(ConstString("_M_p"), true); + finish_offset_sp = m_finish_sp->GetChildMemberWithName(ConstString("_M_offset"), true); + + if (!start_p_sp || !finish_offset_sp || !finish_p_sp) + return false; + + m_base_data_address = start_p_sp->GetValueAsUnsigned(0); + if (!m_base_data_address) + return false; + + lldb::addr_t end_data_address(finish_p_sp->GetValueAsUnsigned(0)); + if (!end_data_address) + return false; + + if (end_data_address < m_base_data_address) + return false; + else + m_count = finish_offset_sp->GetValueAsUnsigned(0) + (end_data_address-m_base_data_address)*8; + + return true; +} + +bool +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEnd::GetIndexOfChildWithName (const 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::formatters::LibstdcppVectorBoolSyntheticFrontEnd::~LibstdcppVectorBoolSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibstdcppVectorBoolSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibstdcppVectorBoolSyntheticFrontEnd(valobj_sp)); +} + +/* + (std::_Rb_tree_iterator, std::allocator > > >) 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 + } + } + */ + +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::LibstdcppMapIteratorSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_exe_ctx_ref(), + m_pair_address(0), + m_pair_type(), + m_options(), + m_pair_sp() +{ + if (valobj_sp) + Update(); + m_options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetKeepInMemory(true) + .SetUseDynamic(lldb::eDynamicCanRunTarget); +} + +bool +lldb_private::formatters::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); + + ClangASTType my_type(valobj_sp->GetClangType()); + if (my_type.GetNumTemplateArguments() >= 1) + { + TemplateArgumentKind kind; + ClangASTType pair_type = my_type.GetTemplateArgument(0, kind); + if (kind != eTemplateArgumentKindType && kind != eTemplateArgumentKindTemplate && kind != eTemplateArgumentKindTemplateExpansion) + return false; + m_pair_type = pair_type; + } + else + return false; + + return true; +} + +size_t +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::CalculateNumChildren () +{ + return 2; +} + +lldb::ValueObjectSP +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (m_pair_address != 0 && m_pair_type) + { + if (!m_pair_sp) + m_pair_sp = ValueObject::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 +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (name == ConstString("first")) + return 0; + if (name == ConstString("second")) + return 1; + return UINT32_MAX; +} + +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEnd::~LibstdcppMapIteratorSyntheticFrontEnd () +{} + +SyntheticChildrenFrontEnd* +lldb_private::formatters::LibstdcppMapIteratorSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + if (!valobj_sp) + return NULL; + return (new LibstdcppMapIteratorSyntheticFrontEnd(valobj_sp)); +} + +/* + (lldb) fr var ibeg --ptr-depth 1 + (__gnu_cxx::__normal_iterator > >) 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"); + if (!valobj_sp) + return NULL; + return (new VectorIteratorSyntheticFrontEnd(valobj_sp,g_item_name)); +} diff --git a/source/DataFormatters/NSArray.cpp b/source/DataFormatters/NSArray.cpp new file mode 100644 index 00000000000..d8ee9bfa8a4 --- /dev/null +++ b/source/DataFormatters/NSArray.cpp @@ -0,0 +1,371 @@ +//===-- NSArray.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +bool +lldb_private::formatters::NSArraySummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + 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; + + uint64_t value = 0; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSArrayI")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else if (!strcmp(class_name,"__NSArrayM")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else if (!strcmp(class_name,"__NSCFArray")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + 2 * ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + } + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) + return false; + } + + stream.Printf("@\"%" PRIu64 " object%s\"", + value, + value == 1 ? "" : "s"); + return true; +} + +lldb_private::formatters::NSArrayMSyntheticFrontEnd::NSArrayMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd(*valobj_sp.get()), + m_exe_ctx_ref(), + m_ptr_size(8), + m_data_32(NULL), + m_data_64(NULL) +{ + if (valobj_sp) + { + clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); + if (ast) + m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); + } +} + +size_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_data_32) + return m_data_32->_used; + if (m_data_64) + return m_data_64->_used; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (!m_data_32 && !m_data_64) + return lldb::ValueObjectSP(); + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx = (m_data_32 ? m_data_32->_data : m_data_64->_data); + size_t pyhs_idx = idx; + pyhs_idx += (m_data_32 ? m_data_32->offset : m_data_64->offset); + if ((m_data_32 ? m_data_32->_size : m_data_64->_size) <= pyhs_idx) + pyhs_idx -= (m_data_32 ? m_data_32->_size : m_data_64->_size); + object_at_idx += (pyhs_idx * m_ptr_size); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), + object_at_idx, + m_exe_ctx_ref, + m_id_type); + m_children.push_back(retval_sp); + return retval_sp; +} + +bool +lldb_private::formatters::NSArrayMSyntheticFrontEnd::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error 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 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::NSArrayMSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSArrayMSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + if (!m_data_32 && !m_data_64) + 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::formatters::NSArrayMSyntheticFrontEnd::~NSArrayMSyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +lldb_private::formatters::NSArrayISyntheticFrontEnd::NSArrayISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : + SyntheticChildrenFrontEnd (*valobj_sp.get()), + m_exe_ctx_ref (), + m_ptr_size (8), + m_items (0), + m_data_ptr (0) +{ + if (valobj_sp) + { + clang::ASTContext *ast = valobj_sp->GetClangType().GetASTContext(); + if (ast) + m_id_type = ClangASTType(ast, ast->ObjCBuiltinIdTy); + } +} + +lldb_private::formatters::NSArrayISyntheticFrontEnd::~NSArrayISyntheticFrontEnd () +{ +} + +size_t +lldb_private::formatters::NSArrayISyntheticFrontEnd::GetIndexOfChildWithName (const 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::NSArrayISyntheticFrontEnd::CalculateNumChildren () +{ + return m_items; +} + +bool +lldb_private::formatters::NSArrayISyntheticFrontEnd::Update() +{ + m_ptr_size = 0; + m_items = 0; + m_data_ptr = 0; + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error 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; + m_items = process_sp->ReadPointerFromMemory(data_location, error); + if (error.Fail()) + return false; + m_data_ptr = data_location+m_ptr_size; + return false; +} + +bool +lldb_private::formatters::NSArrayISyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayISyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + if (idx >= CalculateNumChildren()) + return lldb::ValueObjectSP(); + lldb::addr_t object_at_idx = m_data_ptr; + object_at_idx += (idx * m_ptr_size); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error error; + if (error.Fail()) + return lldb::ValueObjectSP(); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + lldb::ValueObjectSP retval_sp = ValueObject::CreateValueObjectFromAddress(idx_name.GetData(), object_at_idx, m_exe_ctx_ref, m_id_type); + m_children.push_back(retval_sp); + return retval_sp; +} + +SyntheticChildrenFrontEnd* lldb_private::formatters::NSArraySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); + if (!process_sp) + return NULL; + ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (!runtime) + return NULL; + + if (!valobj_sp->IsPointerType()) + { + Error error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return NULL; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return NULL; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return NULL; + + if (!strcmp(class_name,"__NSArrayI")) + { + return (new NSArrayISyntheticFrontEnd(valobj_sp)); + } + else if (!strcmp(class_name,"__NSArrayM")) + { + return (new NSArrayMSyntheticFrontEnd(valobj_sp)); + } + else + { + return (new NSArrayCodeRunningSyntheticFrontEnd(valobj_sp)); + } +} + +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::NSArrayCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()) +{} + +size_t +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::CalculateNumChildren () +{ + uint64_t count = 0; + if (ExtractValueFromObjCExpression(m_backend, "int", "count", count)) + return count; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + lldb::ValueObjectSP valobj_sp = CallSelectorOnObject(m_backend,"id","objectAtIndex:",idx); + if (valobj_sp) + valobj_sp->SetName(ConstString(idx_name.GetData())); + return valobj_sp; +} + +bool +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::Update() +{ + return false; +} + +bool +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return 0; +} + +lldb_private::formatters::NSArrayCodeRunningSyntheticFrontEnd::~NSArrayCodeRunningSyntheticFrontEnd () +{} diff --git a/source/DataFormatters/NSDictionary.cpp b/source/DataFormatters/NSDictionary.cpp new file mode 100644 index 00000000000..05a5dda39e5 --- /dev/null +++ b/source/DataFormatters/NSDictionary.cpp @@ -0,0 +1,579 @@ +//===-- NSDictionary.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/DeclCXX.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +static ClangASTType +GetLLDBNSPairType (TargetSP target_sp) +{ + ClangASTType clang_type; + + ClangASTContext *target_ast_context = target_sp->GetScratchClangASTContext(); + + if (target_ast_context) + { + clang::ASTContext *ast = target_ast_context->getASTContext(); + + if (ast) + { + const char* type_name = "__lldb_autogen_nspair"; + + clang::IdentifierInfo &myIdent = ast->Idents.get(type_name); + clang::DeclarationName myName = ast->DeclarationNames.getIdentifier(&myIdent); + + clang::DeclContext::lookup_const_result result = ast->getTranslationUnitDecl()->lookup(myName); + + for (clang::NamedDecl *named_decl : result) + { + if (const clang::CXXRecordDecl *record_decl = llvm::dyn_cast(named_decl)) + { + clang_type.SetClangType(ast, clang::QualType(record_decl->getTypeForDecl(), 0)); + break; + } + else + { + // somebody else (the user?) has defined a type with the magic name already - fail!!! + return clang_type; + } + } + + if (!clang_type) + { + clang_type = target_ast_context->CreateRecordType(NULL, lldb::eAccessPublic, type_name, clang::TTK_Struct, lldb::eLanguageTypeC); + + if (clang_type) + { + clang_type.StartTagDeclarationDefinition(); + ClangASTType id_clang_type = target_ast_context->GetBasicType (eBasicTypeObjCID); + clang_type.AddFieldToRecordType("key", id_clang_type, lldb::eAccessPublic, 0); + clang_type.AddFieldToRecordType("value", id_clang_type, lldb::eAccessPublic, 0); + clang_type.CompleteTagDeclarationDefinition(); + } + } + } + } + return clang_type; +} + +template +bool +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSDictionaryI")) + { + Error 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,"__NSDictionaryM")) + { + Error 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,"__NSCFDictionary")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error); + if (error.Fail()) + return false; + if (is_64bit) + value &= ~0x0f1f000000000000UL; + }*/ + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) + return false; + } + + stream.Printf("%s%" PRIu64 " %s%s", + (name_entries ? "@\"" : ""), + value, + (name_entries ? (value == 1 ? "entry" : "entries") : (value == 1 ? "key/value pair" : "key/value pairs")), + (name_entries ? "\"" : "")); + return true; +} + +SyntheticChildrenFrontEnd* lldb_private::formatters::NSDictionarySyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); + if (!process_sp) + return NULL; + ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (!runtime) + return NULL; + + if (!valobj_sp->IsPointerType()) + { + Error error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return NULL; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return NULL; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return NULL; + + if (!strcmp(class_name,"__NSDictionaryI")) + { + return (new NSDictionaryISyntheticFrontEnd(valobj_sp)); + } + else if (!strcmp(class_name,"__NSDictionaryM")) + { + return (new NSDictionaryMSyntheticFrontEnd(valobj_sp)); + } + else + { + return (new NSDictionaryCodeRunningSyntheticFrontEnd(valobj_sp)); + } +} + +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::NSDictionaryCodeRunningSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()) +{} + +size_t +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::CalculateNumChildren () +{ + uint64_t count = 0; + if (ExtractValueFromObjCExpression(m_backend, "int", "count", count)) + return count; + return 0; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + StreamString key_fetcher_expr; + key_fetcher_expr.Printf("(id)[(NSArray*)[(id)0x%" PRIx64 " allKeys] objectAtIndex:%zu]",m_backend.GetPointerValue(),idx); + StreamString value_fetcher_expr; + value_fetcher_expr.Printf("(id)[(id)0x%" PRIx64 " objectForKey:(%s)]",m_backend.GetPointerValue(),key_fetcher_expr.GetData()); + StreamString object_fetcher_expr; + object_fetcher_expr.Printf("struct __lldb_autogen_nspair { id key; id value; } _lldb_valgen_item; _lldb_valgen_item.key = %s; _lldb_valgen_item.value = %s; _lldb_valgen_item;",key_fetcher_expr.GetData(),value_fetcher_expr.GetData()); + lldb::ValueObjectSP child_sp; + m_backend.GetTargetSP()->EvaluateExpression(object_fetcher_expr.GetData(), m_backend.GetFrameSP().get(), child_sp, + EvaluateExpressionOptions().SetKeepInMemory(true)); + if (child_sp) + child_sp->SetName(ConstString(idx_name.GetData())); + return child_sp; +} + +bool +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::Update() +{ + return false; +} + +bool +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::GetIndexOfChildWithName (const ConstString &name) +{ + return 0; +} + +lldb_private::formatters::NSDictionaryCodeRunningSyntheticFrontEnd::~NSDictionaryCodeRunningSyntheticFrontEnd () +{} + +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::NSDictionaryISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_order(lldb::eByteOrderInvalid), +m_data_32(NULL), +m_data_64(NULL), +m_pair_type() +{ +} + +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::~NSDictionaryISyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSDictionaryISyntheticFrontEnd::GetIndexOfChildWithName (const 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 = NULL; + delete m_data_64; + m_data_64 = NULL; + m_ptr_size = 0; + ValueObjectSP valobj_sp = m_backend.GetSP(); + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error 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(); + Error 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("[%zu]",idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::NSDictionaryMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_order(lldb::eByteOrderInvalid), +m_data_32(NULL), +m_data_64(NULL), +m_pair_type() +{ +} + +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::~NSDictionaryMSyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSDictionaryMSyntheticFrontEnd::GetIndexOfChildWithName (const 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::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::NSDictionaryMSyntheticFrontEnd::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error 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::NSDictionaryMSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::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(); + Error 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("[%zu]",idx); + DataExtractor data(buffer_sp, m_order, m_ptr_size); + dict_item.valobj_sp = ValueObject::CreateValueObjectFromData(idx_name.GetData(), data, m_exe_ctx_ref, m_pair_type); + } + return dict_item.valobj_sp; +} + +template bool +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject&, Stream&) ; + +template bool +lldb_private::formatters::NSDictionarySummaryProvider (ValueObject&, Stream&) ; diff --git a/source/DataFormatters/NSSet.cpp b/source/DataFormatters/NSSet.cpp new file mode 100644 index 00000000000..02eb2bfc124 --- /dev/null +++ b/source/DataFormatters/NSSet.cpp @@ -0,0 +1,513 @@ +//===-- NSSet.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/CXXFormatterFunctions.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::formatters; + +template +bool +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream) +{ + ProcessSP process_sp = valobj.GetProcessSP(); + if (!process_sp) + return false; + + ObjCLanguageRuntime* runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + + if (!runtime) + return false; + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(valobj)); + + if (!descriptor.get() || !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; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return false; + + if (!strcmp(class_name,"__NSSetI")) + { + Error 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")) + { + Error 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,"__NSCFSet")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + (is_64bit ? 20 : 12), 4, 0, error); + if (error.Fail()) + return false; + if (is_64bit) + value &= ~0x1fff000000000000UL; + } + else if (!strcmp(class_name,"NSCountedSet")) + { + Error error; + value = process_sp->ReadUnsignedIntegerFromMemory(valobj_addr + ptr_size, ptr_size, 0, error); + if (error.Fail()) + return false; + value = process_sp->ReadUnsignedIntegerFromMemory(value + (is_64bit ? 20 : 12), 4, 0, error); + if (error.Fail()) + return false; + if (is_64bit) + value &= ~0x1fff000000000000UL; + }*/ + else + { + if (!ExtractValueFromObjCExpression(valobj, "int", "count", value)) + return false; + } + + stream.Printf("%s%" PRIu64 " %s%s", + (cf_style ? "@\"" : ""), + value, + (cf_style ? (value == 1 ? "value" : "values") : (value == 1 ? "object" : "objects")), + (cf_style ? "\"" : "")); + return true; +} + +SyntheticChildrenFrontEnd* lldb_private::formatters::NSSetSyntheticFrontEndCreator (CXXSyntheticChildren*, lldb::ValueObjectSP valobj_sp) +{ + lldb::ProcessSP process_sp (valobj_sp->GetProcessSP()); + if (!process_sp) + return NULL; + ObjCLanguageRuntime *runtime = (ObjCLanguageRuntime*)process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (!runtime) + return NULL; + + if (!valobj_sp->IsPointerType()) + { + Error error; + valobj_sp = valobj_sp->AddressOf(error); + if (error.Fail() || !valobj_sp) + return NULL; + } + + ObjCLanguageRuntime::ClassDescriptorSP descriptor(runtime->GetClassDescriptor(*valobj_sp.get())); + + if (!descriptor.get() || !descriptor->IsValid()) + return NULL; + + const char* class_name = descriptor->GetClassName().GetCString(); + + if (!class_name || !*class_name) + return NULL; + + if (!strcmp(class_name,"__NSSetI")) + { + return (new NSSetISyntheticFrontEnd(valobj_sp)); + } + else if (!strcmp(class_name,"__NSSetM")) + { + return (new NSSetMSyntheticFrontEnd(valobj_sp)); + } + else if ((!strcmp(class_name,"__NSOrderedSetI")) || (!strcmp(class_name,"__NSOrderedSetM"))) + { + return new NSOrderedSetSyntheticFrontEnd(valobj_sp); // this runs code + } + else + { + return /*(new NSSetCodeRunningSyntheticFrontEnd(valobj_sp))*/ NULL; + } +} + +lldb_private::formatters::NSSetISyntheticFrontEnd::NSSetISyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_data_32(NULL), +m_data_64(NULL) +{ + if (valobj_sp) + Update(); +} + +lldb_private::formatters::NSSetISyntheticFrontEnd::~NSSetISyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSSetISyntheticFrontEnd::GetIndexOfChildWithName (const 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 = NULL; + delete m_data_64; + m_data_64 = NULL; + 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(); + Error 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(); + + 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); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error 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) + { + // make the new ValueObject + StreamString expr; + expr.Printf("(id)%" PRIu64,set_item.item_ptr); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref); + } + return set_item.valobj_sp; +} + +lldb_private::formatters::NSSetMSyntheticFrontEnd::NSSetMSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_exe_ctx_ref(), +m_ptr_size(8), +m_data_32(NULL), +m_data_64(NULL) +{ + if (valobj_sp) + Update (); +} + +lldb_private::formatters::NSSetMSyntheticFrontEnd::~NSSetMSyntheticFrontEnd () +{ + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; +} + +size_t +lldb_private::formatters::NSSetMSyntheticFrontEnd::GetIndexOfChildWithName (const 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::NSSetMSyntheticFrontEnd::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::NSSetMSyntheticFrontEnd::Update() +{ + m_children.clear(); + ValueObjectSP valobj_sp = m_backend.GetSP(); + m_ptr_size = 0; + delete m_data_32; + m_data_32 = NULL; + delete m_data_64; + m_data_64 = NULL; + if (!valobj_sp) + return false; + if (!valobj_sp) + return false; + m_exe_ctx_ref = valobj_sp->GetExecutionContextRef(); + Error 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; + return false; +} + +bool +lldb_private::formatters::NSSetMSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +lldb::ValueObjectSP +lldb_private::formatters::NSSetMSyntheticFrontEnd::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(); + + 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); + ProcessSP process_sp = m_exe_ctx_ref.GetProcessSP(); + if (!process_sp) + return lldb::ValueObjectSP(); + Error 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) + { + // make the new ValueObject + StreamString expr; + expr.Printf("(id)%" PRIu64,set_item.item_ptr); + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + set_item.valobj_sp = ValueObject::CreateValueObjectFromExpression(idx_name.GetData(), expr.GetData(), m_exe_ctx_ref); + } + return set_item.valobj_sp; +} + +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::NSOrderedSetSyntheticFrontEnd (lldb::ValueObjectSP valobj_sp) : +SyntheticChildrenFrontEnd(*valobj_sp.get()), +m_count(UINT32_MAX), +m_children() +{} + +size_t +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::CalculateNumChildren () +{ + if (m_count != UINT32_MAX) + return m_count; + uint64_t count_temp; + if (ExtractValueFromObjCExpression(m_backend,"unsigned int","count",count_temp)) + return (m_count = count_temp); + return (m_count = 0); +} + +lldb::ValueObjectSP +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetChildAtIndex (size_t idx) +{ + auto iter = m_children.find(idx); + if (iter == m_children.end()) + { + lldb::ValueObjectSP retval_sp; + if (idx <= m_count) + { + retval_sp = CallSelectorOnObject(m_backend, "id", "objectAtIndex", idx); + if (retval_sp) + { + StreamString idx_name; + idx_name.Printf("[%zu]",idx); + retval_sp->SetName(ConstString(idx_name.GetData())); + } + m_children[idx] = retval_sp; + } + return retval_sp; + } + else + return iter->second; +} + +bool +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::Update() +{ + return false; +} + +bool +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::MightHaveChildren () +{ + return true; +} + +size_t +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::GetIndexOfChildWithName (const 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; +} + +lldb_private::formatters::NSOrderedSetSyntheticFrontEnd::~NSOrderedSetSyntheticFrontEnd () +{ +} + +template bool +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream); + +template bool +lldb_private::formatters::NSSetSummaryProvider (ValueObject& valobj, Stream& stream); diff --git a/source/DataFormatters/TypeCategory.cpp b/source/DataFormatters/TypeCategory.cpp new file mode 100644 index 00000000000..c887be5da49 --- /dev/null +++ b/source/DataFormatters/TypeCategory.cpp @@ -0,0 +1,382 @@ +//===-- TypeCategory.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/TypeCategory.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +TypeCategoryImpl::TypeCategoryImpl(IFormatChangeListener* clist, + ConstString name) : +m_summary_nav(new SummaryNavigator("summary",clist)), +m_regex_summary_nav(new RegexSummaryNavigator("regex-summary",clist)), +m_filter_nav(new FilterNavigator("filter",clist)), +m_regex_filter_nav(new RegexFilterNavigator("regex-filter",clist)), +#ifndef LLDB_DISABLE_PYTHON +m_synth_nav(new SynthNavigator("synth",clist)), +m_regex_synth_nav(new RegexSynthNavigator("regex-synth",clist)), +#endif +m_enabled(false), +m_change_listener(clist), +m_mutex(Mutex::eMutexTypeRecursive), +m_name(name) +{} + +bool +TypeCategoryImpl::Get (ValueObject& valobj, + lldb::TypeSummaryImplSP& entry, + lldb::DynamicValueType use_dynamic, + uint32_t* reason) +{ + if (!IsEnabled()) + return false; + if (GetSummaryNavigator()->Get(valobj, entry, use_dynamic, reason)) + return true; + bool regex = GetRegexSummaryNavigator()->Get(valobj, entry, use_dynamic, reason); + if (regex && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionSummary; + return regex; +} + +bool +TypeCategoryImpl::Get(ValueObject& valobj, + lldb::SyntheticChildrenSP& entry_sp, + lldb::DynamicValueType use_dynamic, + uint32_t* reason) +{ + if (!IsEnabled()) + 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 (!GetFilterNavigator()->Get(valobj, filter_sp, use_dynamic, &reason_filter)) + regex_filter = GetRegexFilterNavigator()->Get (valobj, filter_sp, use_dynamic, &reason_filter); + +#ifndef LLDB_DISABLE_PYTHON + bool regex_synth = false; + uint32_t reason_synth = 0; + bool pick_synth = false; + ScriptedSyntheticChildren::SharedPointer synth; + if (!GetSyntheticNavigator()->Get(valobj, synth, use_dynamic, &reason_synth)) + regex_synth = GetRegexSyntheticNavigator()->Get (valobj, synth, use_dynamic, &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())*/ + { + if (filter_sp->GetRevision() > synth->GetRevision()) + pick_synth = false; + else + pick_synth = true; + } + if (pick_synth) + { + if (regex_synth && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter; + entry_sp = synth; + return true; + } + else + { + if (regex_filter && reason) + *reason |= lldb_private::eFormatterChoiceCriterionRegularExpressionFilter; + entry_sp = filter_sp; + return true; + } + +#else + if (filter_sp) + { + entry_sp = filter_sp; + return true; + } +#endif + + return false; + +} + +void +TypeCategoryImpl::Clear (FormatCategoryItems items) +{ + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + m_summary_nav->Clear(); + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + m_regex_summary_nav->Clear(); + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + m_filter_nav->Clear(); + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + m_regex_filter_nav->Clear(); +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + m_synth_nav->Clear(); + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + m_regex_synth_nav->Clear(); +#endif +} + +bool +TypeCategoryImpl::Delete (ConstString name, + FormatCategoryItems items) +{ + bool success = false; + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + success = m_summary_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + success = m_regex_summary_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + success = m_filter_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + success = m_regex_filter_nav->Delete(name) || success; +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + success = m_synth_nav->Delete(name) || success; + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + success = m_regex_synth_nav->Delete(name) || success; +#endif + return success; +} + +uint32_t +TypeCategoryImpl::GetCount (FormatCategoryItems items) +{ + uint32_t count = 0; + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + count += m_summary_nav->GetCount(); + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + count += m_regex_summary_nav->GetCount(); + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + count += m_filter_nav->GetCount(); + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + count += m_regex_filter_nav->GetCount(); +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + count += m_synth_nav->GetCount(); + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + count += m_regex_synth_nav->GetCount(); +#endif + 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::TypeSummaryImplSP summary; + TypeFilterImpl::SharedPointer filter; +#ifndef LLDB_DISABLE_PYTHON + ScriptedSyntheticChildren::SharedPointer synth; +#endif + + if ( (items & eFormatCategoryItemSummary) == eFormatCategoryItemSummary ) + { + if (m_summary_nav->Get(type_name, summary)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemSummary; + return true; + } + } + if ( (items & eFormatCategoryItemRegexSummary) == eFormatCategoryItemRegexSummary ) + { + if (m_regex_summary_nav->Get(type_name, summary)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexSummary; + return true; + } + } + if ( (items & eFormatCategoryItemFilter) == eFormatCategoryItemFilter ) + { + if (m_filter_nav->Get(type_name, filter)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemFilter; + return true; + } + } + if ( (items & eFormatCategoryItemRegexFilter) == eFormatCategoryItemRegexFilter ) + { + if (m_regex_filter_nav->Get(type_name, filter)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexFilter; + return true; + } + } +#ifndef LLDB_DISABLE_PYTHON + if ( (items & eFormatCategoryItemSynth) == eFormatCategoryItemSynth ) + { + if (m_synth_nav->Get(type_name, synth)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemSynth; + return true; + } + } + if ( (items & eFormatCategoryItemRegexSynth) == eFormatCategoryItemRegexSynth ) + { + if (m_regex_synth_nav->Get(type_name, synth)) + { + if (matching_category) + *matching_category = m_name.GetCString(); + if (matching_type) + *matching_type = eFormatCategoryItemRegexSynth; + return true; + } + } +#endif + return false; +} + +TypeCategoryImpl::SummaryNavigator::MapValueType +TypeCategoryImpl::GetSummaryForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + SummaryNavigator::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + m_regex_summary_nav->GetExact(ConstString(type_sp->GetName()),retval); + else + m_summary_nav->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} + +TypeCategoryImpl::FilterNavigator::MapValueType +TypeCategoryImpl::GetFilterForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + FilterNavigator::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + m_regex_filter_nav->GetExact(ConstString(type_sp->GetName()),retval); + else + m_filter_nav->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} + +#ifndef LLDB_DISABLE_PYTHON +TypeCategoryImpl::SynthNavigator::MapValueType +TypeCategoryImpl::GetSyntheticForType (lldb::TypeNameSpecifierImplSP type_sp) +{ + SynthNavigator::MapValueType retval; + + if (type_sp) + { + if (type_sp->IsRegex()) + m_regex_synth_nav->GetExact(ConstString(type_sp->GetName()),retval); + else + m_synth_nav->GetExact(ConstString(type_sp->GetName()),retval); + } + + return retval; +} +#endif + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForSummaryAtIndex (size_t index) +{ + if (index < m_summary_nav->GetCount()) + return m_summary_nav->GetTypeNameSpecifierAtIndex(index); + else + return m_regex_summary_nav->GetTypeNameSpecifierAtIndex(index-m_summary_nav->GetCount()); +} + +TypeCategoryImpl::SummaryNavigator::MapValueType +TypeCategoryImpl::GetSummaryAtIndex (size_t index) +{ + if (index < m_summary_nav->GetCount()) + return m_summary_nav->GetAtIndex(index); + else + return m_regex_summary_nav->GetAtIndex(index-m_summary_nav->GetCount()); +} + +TypeCategoryImpl::FilterNavigator::MapValueType +TypeCategoryImpl::GetFilterAtIndex (size_t index) +{ + if (index < m_filter_nav->GetCount()) + return m_filter_nav->GetAtIndex(index); + else + return m_regex_filter_nav->GetAtIndex(index-m_filter_nav->GetCount()); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForFilterAtIndex (size_t index) +{ + if (index < m_filter_nav->GetCount()) + return m_filter_nav->GetTypeNameSpecifierAtIndex(index); + else + return m_regex_filter_nav->GetTypeNameSpecifierAtIndex(index-m_filter_nav->GetCount()); +} + +#ifndef LLDB_DISABLE_PYTHON +TypeCategoryImpl::SynthNavigator::MapValueType +TypeCategoryImpl::GetSyntheticAtIndex (size_t index) +{ + if (index < m_synth_nav->GetCount()) + return m_synth_nav->GetAtIndex(index); + else + return m_regex_synth_nav->GetAtIndex(index-m_synth_nav->GetCount()); +} + +lldb::TypeNameSpecifierImplSP +TypeCategoryImpl::GetTypeNameSpecifierForSyntheticAtIndex (size_t index) +{ + if (index < m_synth_nav->GetCount()) + return m_synth_nav->GetTypeNameSpecifierAtIndex(index); + else + return m_regex_synth_nav->GetTypeNameSpecifierAtIndex(index - m_synth_nav->GetCount()); +} +#endif + +void +TypeCategoryImpl::Enable (bool value, uint32_t position) +{ + Mutex::Locker locker(m_mutex); + m_enabled = value; + m_enabled_position = position; + if (m_change_listener) + m_change_listener->Changed(); +} diff --git a/source/DataFormatters/TypeCategoryMap.cpp b/source/DataFormatters/TypeCategoryMap.cpp new file mode 100644 index 00000000000..4b7222adbec --- /dev/null +++ b/source/DataFormatters/TypeCategoryMap.cpp @@ -0,0 +1,285 @@ +//===-- TypeCategoryMap.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/DataFormatters/TypeCategoryMap.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +using namespace lldb; +using namespace lldb_private; + +TypeCategoryMap::TypeCategoryMap (IFormatChangeListener* lst) : +m_map_mutex(Mutex::eMutexTypeRecursive), +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) +{ + Mutex::Locker locker(m_map_mutex); + m_map[name] = entry; + if (listener) + listener->Changed(); +} + +bool +TypeCategoryMap::Delete (KeyType name) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(m_map_mutex); + ValueSP category; + if (!Get(category_name,category)) + return false; + return Enable(category, pos); +} + +bool +TypeCategoryMap::Disable (KeyType category_name) +{ + Mutex::Locker locker(m_map_mutex); + ValueSP category; + if (!Get(category_name,category)) + return false; + return Disable(category); +} + +bool +TypeCategoryMap::Enable (ValueSP category, Position pos) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(m_map_mutex); + if (category.get()) + { + m_active_categories.remove_if(delete_matching_categories(category)); + category->Disable(); + return true; + } + return false; +} + +void +TypeCategoryMap::Clear () +{ + Mutex::Locker locker(m_map_mutex); + m_map.clear(); + m_active_categories.clear(); + if (listener) + listener->Changed(); +} + +bool +TypeCategoryMap::Get (KeyType name, ValueSP& entry) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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) +{ + Mutex::Locker locker(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::TypeSummaryImplSP +TypeCategoryMap::GetSummaryFormat (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + Mutex::Locker locker(m_map_mutex); + + uint32_t reason_why; + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + lldb::TypeCategoryImplSP category_sp = *begin; + lldb::TypeSummaryImplSP current_format; + if (log) + log->Printf("\n[CategoryMap::GetSummaryFormat] Trying to use category %s", category_sp->GetName()); + if (!category_sp->Get(valobj, current_format, use_dynamic, &reason_why)) + continue; + return current_format; + } + if (log) + log->Printf("[CategoryMap::GetSummaryFormat] nothing found - returning empty SP"); + return lldb::TypeSummaryImplSP(); +} + +#ifndef LLDB_DISABLE_PYTHON +lldb::SyntheticChildrenSP +TypeCategoryMap::GetSyntheticChildren (ValueObject& valobj, + lldb::DynamicValueType use_dynamic) +{ + Mutex::Locker locker(m_map_mutex); + + uint32_t reason_why; + + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + lldb::TypeCategoryImplSP category_sp = *begin; + lldb::SyntheticChildrenSP current_format; + if (log) + log->Printf("\n[CategoryMap::GetSyntheticChildren] Trying to use category %s", category_sp->GetName()); + if (!category_sp->Get(valobj, current_format, use_dynamic, &reason_why)) + continue; + return current_format; + } + if (log) + log->Printf("[CategoryMap::GetSyntheticChildren] nothing found - returning empty SP"); + return lldb::SyntheticChildrenSP(); +} +#endif + +void +TypeCategoryMap::LoopThrough(CallbackType callback, void* param) +{ + if (callback) + { + Mutex::Locker locker(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; + ConstString type = ConstString(category->GetName()); + if (!callback(param, 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; + KeyType type = pos->first; + if (!callback(param, pos->second)) + break; + } + } + } +} + +TypeCategoryImplSP +TypeCategoryMap::GetAtIndex (uint32_t index) +{ + Mutex::Locker locker(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/source/DataFormatters/TypeFormat.cpp b/source/DataFormatters/TypeFormat.cpp new file mode 100644 index 00000000000..279704f2af1 --- /dev/null +++ b/source/DataFormatters/TypeFormat.cpp @@ -0,0 +1,52 @@ +//===-- TypeFormat.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/DataFormatters/TypeFormat.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +TypeFormatImpl::TypeFormatImpl (lldb::Format f, + const Flags& flags) : +m_flags(flags), +m_format (f) +{ +} + +std::string +TypeFormatImpl::GetDescription() +{ + StreamString sstr; + sstr.Printf ("%s%s%s%s\n", + FormatManager::GetFormatAsCString (GetFormat()), + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : ""); + return sstr.GetString(); +} + diff --git a/source/DataFormatters/TypeSummary.cpp b/source/DataFormatters/TypeSummary.cpp new file mode 100644 index 00000000000..8c4d3f71c05 --- /dev/null +++ b/source/DataFormatters/TypeSummary.cpp @@ -0,0 +1,250 @@ +//===-- TypeSummary.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/DataFormatters/TypeSummary.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +#include "lldb/Host/Host.h" + +using namespace lldb; +using namespace lldb_private; + +TypeSummaryImpl::TypeSummaryImpl (const TypeSummaryImpl::Flags& flags) : +m_flags(flags) +{ +} + + +StringSummaryFormat::StringSummaryFormat (const TypeSummaryImpl::Flags& flags, + const char *format_cstr) : +TypeSummaryImpl(flags), +m_format() +{ + if (format_cstr) + m_format.assign(format_cstr); +} + +bool +StringSummaryFormat::FormatObject (ValueObject *valobj, + std::string& retval) +{ + 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()) + { + ValueObject* object; + + ValueObjectSP synth_valobj = valobj->GetSyntheticValue(); + if (synth_valobj) + object = synth_valobj.get(); + else + object = valobj; + + const uint32_t num_children = object->GetNumChildren(); + if (num_children) + { + s.PutChar('('); + + for (uint32_t idx=0; idxGetChildAtIndex(idx, true)); + if (child_sp.get()) + { + if (idx) + s.PutCString(", "); + if (!HideNames()) + { + s.PutCString(child_sp.get()->GetName().AsCString()); + s.PutCString(" = "); + } + child_sp.get()->DumpPrintableRepresentation(s, + ValueObject::eValueObjectRepresentationStyleSummary, + lldb::eFormatInvalid, + ValueObject::ePrintableRepresentationSpecialCasesDisable); + } + } + + s.PutChar(')'); + + retval.assign(s.GetString()); + return true; + } + else + { + retval.assign("error: oneliner for no children"); + return false; + } + + } + else + { + if (Debugger::FormatPrompt(m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, valobj)) + { + 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", m_format.c_str(), + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren() ? "" : " (show children)", + !DoesPrintValue() ? " (hide value)" : "", + IsOneliner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames() ? " (hide member names)" : ""); + return sstr.GetString(); +} + +CXXFunctionSummaryFormat::CXXFunctionSummaryFormat (const TypeSummaryImpl::Flags& flags, + Callback impl, + const char* description) : +TypeSummaryImpl(flags), +m_impl(impl), +m_description(description ? description : "") +{ +} + +bool +CXXFunctionSummaryFormat::FormatObject (ValueObject *valobj, + std::string& dest) +{ + dest.clear(); + StreamString stream; + if (!m_impl || m_impl(*valobj,stream) == false) + return false; + dest.assign(stream.GetData()); + return true; +} + +std::string +CXXFunctionSummaryFormat::GetDescription () +{ + StreamString sstr; + sstr.Printf ("`%s (%p) `%s%s%s%s%s%s%s", m_description.c_str(),m_impl, + Cascades() ? "" : " (not cascading)", + !DoesPrintChildren() ? "" : " (show children)", + !DoesPrintValue() ? " (hide value)" : "", + IsOneliner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames() ? " (hide member names)" : ""); + return sstr.GetString(); +} + +#ifndef LLDB_DISABLE_PYTHON + + +ScriptSummaryFormat::ScriptSummaryFormat (const TypeSummaryImpl::Flags& flags, + const char * function_name, + const char * python_script) : +TypeSummaryImpl(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) +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + if (!valobj) + return false; + + Host::SetCrashDescriptionWithFormat("[Python summary] Name: %s - Function: %s", + valobj->GetName().AsCString("unknown"), + m_function_name.c_str()); + + TargetSP target_sp(valobj->GetTargetSP()); + + if (!target_sp) + { + retval.assign("error: no target"); + return false; + } + + ScriptInterpreter *script_interpreter = target_sp->GetDebugger().GetCommandInterpreter().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, + retval); + +} + +std::string +ScriptSummaryFormat::GetDescription () +{ + StreamString sstr; + sstr.Printf ("%s%s%s%s%s%s%s\n%s", Cascades() ? "" : " (not cascading)", + !DoesPrintChildren() ? "" : " (show children)", + !DoesPrintValue() ? " (hide value)" : "", + IsOneliner() ? " (one-line printout)" : "", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + HideNames() ? " (hide member names)" : "", + m_python_script.c_str()); + return sstr.GetString(); + +} + +#endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/source/DataFormatters/TypeSynthetic.cpp b/source/DataFormatters/TypeSynthetic.cpp new file mode 100644 index 00000000000..972be486b78 --- /dev/null +++ b/source/DataFormatters/TypeSynthetic.cpp @@ -0,0 +1,114 @@ +//===-- TypeSynthetic.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes + +// C++ Includes + +// Other libraries and framework includes + +// Project includes +#include "lldb/lldb-public.h" +#include "lldb/lldb-enumerations.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Core/StreamString.h" +#include "lldb/DataFormatters/TypeSynthetic.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +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 Generator at %p - %s", + Cascades() ? "" : " (not cascading)", + SkipsPointers() ? " (skip pointers)" : "", + SkipsReferences() ? " (skip references)" : "", + m_create_callback, + m_description.c_str()); + + return sstr.GetString(); +} + +#ifndef LLDB_DISABLE_PYTHON + +ScriptedSyntheticChildren::FrontEnd::FrontEnd(std::string pclass, ValueObject &backend) : +SyntheticChildrenFrontEnd(backend), +m_python_class(pclass), +m_wrapper_sp(), +m_interpreter(NULL) +{ + if (backend == LLDB_INVALID_UID) + return; + + TargetSP target_sp = backend.GetTargetSP(); + + if (!target_sp) + return; + + m_interpreter = target_sp->GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + + if (m_interpreter != NULL) + 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); +} + +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(); +} + +#endif // #ifndef LLDB_DISABLE_PYTHON diff --git a/source/Expression/ASTDumper.cpp b/source/Expression/ASTDumper.cpp new file mode 100644 index 00000000000..5210d149e66 --- /dev/null +++ b/source/Expression/ASTDumper.cpp @@ -0,0 +1,132 @@ +//===-- ASTDumper.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Symbol/ClangASTType.h" + +#include "llvm/Support/raw_ostream.h" + +using namespace lldb_private; + +ASTDumper::ASTDumper (clang::Decl *decl) +{ + clang::DeclContext *decl_ctx = llvm::dyn_cast(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(decl_ctx)) + { + llvm::raw_string_ostream os(m_dump); + decl->print (os); + os.flush(); + } + else + { + m_dump.assign(""); + } + + 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::clang_type_t type) +{ + m_dump = clang::QualType::getFromOpaquePtr(type).getAsString(); +} + +ASTDumper::ASTDumper (const ClangASTType &clang_type) +{ + m_dump = clang_type.GetQualType().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 = NULL; + + 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.c_str()); +} diff --git a/source/Expression/ASTResultSynthesizer.cpp b/source/Expression/ASTResultSynthesizer.cpp new file mode 100644 index 00000000000..76c2577af53 --- /dev/null +++ b/source/Expression/ASTResultSynthesizer.cpp @@ -0,0 +1,512 @@ +//===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/ASTResultSynthesizer.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTImporter.h" +#include "lldb/Target/Target.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough, + Target &target) : + m_ast_context (NULL), + m_passthrough (passthrough), + m_passthrough_sema (NULL), + m_target (target), + m_sema (NULL) +{ + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast(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(D)) + { + if (log && log->GetVerbose()) + { + if (named_decl->getIdentifier()) + log->Printf("TransformTopLevelDecl(%s)", named_decl->getIdentifier()->getNameStart()); + else if (ObjCMethodDecl *method_decl = dyn_cast(D)) + log->Printf("TransformTopLevelDecl(%s)", method_decl->getSelector().getAsString().c_str()); + else + log->Printf("TransformTopLevelDecl()"); + } + + } + + if (LinkageSpecDecl *linkage_spec_decl = dyn_cast(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 (ObjCMethodDecl *method_decl = dyn_cast(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(D)) + { + if (m_ast_context && + !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(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(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(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(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(last_expr); + + if (!implicit_cast) + break; + + if (implicit_cast->getCastKind() != CK_LValueToRValue) + break; + + last_expr = implicit_cast->getSubExpr(); + } while (0); + + // 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->getValueKind() == VK_XValue) && + (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 = NULL; + + 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() != NULL) + 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, + NULL, + SC_Static); + + if (!result_decl) + return false; + + ExprResult address_of_expr = m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr); + + m_sema->AddInitializerToDecl(result_decl, address_of_expr.take(), true, false); + } + else + { + IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result"); + + result_decl = VarDecl::Create(Ctx, + DC, + SourceLocation(), + SourceLocation(), + &result_id, + expr_qual_type, + NULL, + SC_Static); + + if (!result_decl) + return false; + + m_sema->AddInitializerToDecl(result_decl, last_expr, true, false); + } + + 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(result_initialization_stmt_result.take()); + + return true; +} + +void +ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) +{ + if (m_passthrough) + m_passthrough->HandleTranslationUnit(Ctx); +} + +void +ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) +{ + typedef DeclContext::specific_decl_iterator 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()); + + Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl(m_target.GetScratchClangASTContext()->getASTContext(), + m_ast_context, + D); + + if (TypeDecl *TypeDecl_scratch = dyn_cast(D_scratch)) + m_target.GetPersistentVariables().RegisterPersistentType(name_cs, TypeDecl_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, bool DefinitionRequired) +{ + if (m_passthrough) + m_passthrough->HandleVTable(RD, DefinitionRequired); +} + +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 = NULL; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/source/Expression/ASTStructExtractor.cpp b/source/Expression/ASTStructExtractor.cpp new file mode 100644 index 00000000000..d1f21923deb --- /dev/null +++ b/source/Expression/ASTStructExtractor.cpp @@ -0,0 +1,220 @@ +//===-- ASTStructExtractor.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ASTStructExtractor.h" + +using namespace llvm; +using namespace clang; +using namespace lldb_private; + +ASTStructExtractor::ASTStructExtractor(ASTConsumer *passthrough, + const char *struct_name, + ClangFunction &function) : + m_ast_context (NULL), + m_passthrough (passthrough), + m_passthrough_sema (NULL), + m_sema (NULL), + m_action (NULL), + m_function (function), + m_struct_name (struct_name) +{ + if (!m_passthrough) + return; + + m_passthrough_sema = dyn_cast(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(body_stmt); + + if (!body_compound_stmt) + return; // do we have to handle this? + + RecordDecl *struct_decl = NULL; + + StringRef desired_name(m_struct_name.c_str()); + + 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(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(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(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(D); + + if (m_ast_context && + function_decl && + !m_function.m_wrapper_function_name.compare(function_decl->getNameAsString().c_str())) + { + 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, bool DefinitionRequired) +{ + if (m_passthrough) + m_passthrough->HandleVTable(RD, DefinitionRequired); +} + +void +ASTStructExtractor::PrintStats() +{ + if (m_passthrough) + m_passthrough->PrintStats(); +} + +void +ASTStructExtractor::InitializeSema(Sema &S) +{ + m_sema = &S; + m_action = reinterpret_cast(m_sema); + + if (m_passthrough_sema) + m_passthrough_sema->InitializeSema(S); +} + +void +ASTStructExtractor::ForgetSema() +{ + m_sema = NULL; + m_action = NULL; + + if (m_passthrough_sema) + m_passthrough_sema->ForgetSema(); +} diff --git a/source/Expression/ClangASTSource.cpp b/source/Expression/ClangASTSource.cpp new file mode 100644 index 00000000000..49513d740f3 --- /dev/null +++ b/source/Expression/ClangASTSource.cpp @@ -0,0 +1,1866 @@ +//===-- ClangASTSource.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleList.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/SymbolVendor.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Target.h" + +using namespace clang; +using namespace lldb_private; + +ClangASTSource::~ClangASTSource() +{ + m_ast_importer->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->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. Not important for now. + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXLiteralOperatorName: + SetNoExternalVisibleDeclsForName(decl_ctx, clang_decl_name); + return false; + + // 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 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: + 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 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, + m_ast_context, + tag_decl, + tag_decl->getName().str().c_str()); + + log->Printf(" CTD[%u] Before:", current_id); + ASTDumper dumper((Decl*)tag_decl); + dumper.ToLog(log, " [CTD] "); + } + + if (!m_ast_importer->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(decl_ctx)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CTD[%u] Inspecting namespace map %p (%d entries)", + current_id, + namespace_map.get(), + (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.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + TypeList types; + + SymbolContext null_sc; + ConstString name(tag_decl->getName().str().c_str()); + + i->first->FindTypesInNamespace(null_sc, 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; + + ClangASTType clang_type (type->GetClangFullType()); + + if (!clang_type) + continue; + + const TagType *tag_type = clang_type.GetQualType()->getAs(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = const_cast(tag_type->getDecl()); + + if (m_ast_importer->CompleteTagDeclWithOrigin (tag_decl, candidate_tag_decl)) + found = true; + } + } + } + else + { + TypeList types; + + SymbolContext null_sc; + ConstString name(tag_decl->getName().str().c_str()); + ClangNamespaceDecl namespace_decl; + + const ModuleList &module_list = m_target->GetImages(); + + bool exact_match = false; + module_list.FindTypes (null_sc, name, exact_match, UINT32_MAX, types); + + for (uint32_t ti = 0, te = types.GetSize(); + ti != te && !found; + ++ti) + { + lldb::TypeSP type = types.GetTypeAtIndex(ti); + + if (!type) + continue; + + ClangASTType clang_type (type->GetClangFullType()); + + if (!clang_type) + continue; + + const TagType *tag_type = clang_type.GetQualType()->getAs(); + + if (!tag_type) + continue; + + TagDecl *candidate_tag_decl = const_cast(tag_type->getDecl()); + + if (m_ast_importer->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", m_ast_context, interface_decl->getName().str().c_str()); + log->Printf(" [COID] Before:"); + ASTDumper dumper((Decl*)interface_decl); + dumper.ToLog(log, " [COID] "); + } + + m_ast_importer->CompleteObjCInterfaceDecl (interface_decl); + + if (log) + { + log->Printf(" [COID] After:"); + ASTDumper dumper((Decl*)interface_decl); + dumper.ToLog(log, " [COID] "); + } +} + +clang::ObjCInterfaceDecl * +ClangASTSource::GetCompleteObjCInterface (clang::ObjCInterfaceDecl *interface_decl) +{ + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return NULL; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + return NULL; + + ConstString class_name(interface_decl->getNameAsString().c_str()); + + lldb::TypeSP complete_type_sp(language_runtime->LookupInCompleteClassCache(class_name)); + + if (!complete_type_sp) + return NULL; + + TypeFromUser complete_type = TypeFromUser(complete_type_sp->GetClangFullType()); + lldb::clang_type_t complete_opaque_type = complete_type.GetOpaqueQualType(); + + if (!complete_opaque_type) + return NULL; + + const clang::Type *complete_clang_type = QualType::getFromOpaquePtr(complete_opaque_type).getTypePtr(); + const ObjCInterfaceType *complete_interface_type = dyn_cast(complete_clang_type); + + if (!complete_interface_type) + return NULL; + + ObjCInterfaceDecl *complete_iface_decl(complete_interface_type->getDecl()); + + return complete_iface_decl; +} + +clang::ExternalLoadResult +ClangASTSource::FindExternalLexicalDecls (const DeclContext *decl_context, + bool (*predicate)(Decl::Kind), + llvm::SmallVectorImpl &decls) +{ + ClangASTMetrics::RegisterLexicalQuery(); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + const Decl *context_decl = dyn_cast(decl_context); + + if (!context_decl) + return ELR_Failure; + + static unsigned int invocation_id = 0; + unsigned int current_id = invocation_id++; + + if (log) + { + if (const NamedDecl *context_named_decl = dyn_cast(context_decl)) + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in '%s' (%sDecl*)%p with %s predicate", + current_id, + m_ast_context, + context_named_decl->getNameAsString().c_str(), + context_decl->getDeclKindName(), + context_decl, + (predicate ? "non-null" : "null")); + else if(context_decl) + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in (%sDecl*)%p with %s predicate", + current_id, + m_ast_context, + context_decl->getDeclKindName(), + context_decl, + (predicate ? "non-null" : "null")); + else + log->Printf("FindExternalLexicalDecls[%u] on (ASTContext*)%p in a NULL context with %s predicate", + current_id, + m_ast_context, + (predicate ? "non-null" : "null")); + } + + Decl *original_decl = NULL; + ASTContext *original_ctx = NULL; + + if (!m_ast_importer->ResolveDeclOrigin(context_decl, &original_decl, &original_ctx)) + return ELR_Failure; + + if (log) + { + log->Printf(" FELD[%u] Original decl (ASTContext*)%p (Decl*)%p:", current_id, original_ctx, original_decl); + ASTDumper(original_decl).ToLog(log, " "); + } + + if (ObjCInterfaceDecl *original_iface_decl = dyn_cast(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->SetDeclOrigin(context_decl, original_iface_decl); + } + } + + if (TagDecl *original_tag_decl = dyn_cast(original_decl)) + { + ExternalASTSource *external_source = original_ctx->getExternalSource(); + + if (external_source) + external_source->CompleteType (original_tag_decl); + } + + const DeclContext *original_decl_context = dyn_cast(original_decl); + + if (!original_decl_context) + return ELR_Failure; + + for (TagDecl::decl_iterator iter = original_decl_context->decls_begin(); + iter != original_decl_context->decls_end(); + ++iter) + { + Decl *decl = *iter; + + if (!predicate || predicate(decl->getKind())) + { + if (log) + { + ASTDumper ast_dumper(decl); + if (const NamedDecl *context_named_decl = dyn_cast(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 = m_ast_importer->CopyDecl(m_ast_context, original_ctx, decl); + + if (!copied_decl) + continue; + + if (FieldDecl *copied_field = dyn_cast(copied_decl)) + { + QualType copied_field_type = copied_field->getType(); + + m_ast_importer->RequireCompleteType(copied_field_type); + } + + decls.push_back(copied_decl); + + DeclContext *decl_context_non_const = const_cast(decl_context); + + if (copied_decl->getDeclContext() != decl_context) + { + if (copied_decl->getDeclContext()->containsDecl(copied_decl)) + copied_decl->getDeclContext()->removeDecl(copied_decl); + copied_decl->setDeclContext(decl_context_non_const); + } + + if (!decl_context_non_const->containsDecl(copied_decl)) + decl_context_non_const->addDeclInternal(copied_decl); + } + } + + return ELR_AlreadyLoaded; +} + +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, m_ast_context, name.GetCString()); + else if (const NamedDecl *context_named_decl = dyn_cast(context.m_decl_context)) + log->Printf("ClangASTSource::FindExternalVisibleDecls[%u] on (ASTContext*)%p for '%s' in '%s'", current_id, 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, m_ast_context, name.GetCString(), context.m_decl_context->getDeclKindName()); + } + + context.m_namespace_map.reset(new ClangASTImporter::NamespaceMap); + + if (const NamespaceDecl *namespace_context = dyn_cast(context.m_decl_context)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CAS::FEVD[%u] Inspecting namespace map %p (%d entries)", + current_id, + namespace_map.get(), + (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.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, + i->first, + i->second, + current_id); + } + } + else if (isa(context.m_decl_context)) + { + FindObjCPropertyAndIvarDecls(context); + } + else if (!isa(context.m_decl_context)) + { + // we shouldn't be getting FindExternalVisibleDecls calls for these + return; + } + else + { + ClangNamespaceDecl 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, + context.m_namespace_map.get(), + (int)context.m_namespace_map->size()); + + NamespaceDecl *clang_namespace_decl = AddNamespace(context, context.m_namespace_map); + + if (clang_namespace_decl) + clang_namespace_decl->setHasExternalVisibleStorage(); + } +} + +void +ClangASTSource::FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module_sp, + ClangNamespaceDecl &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()); + + const char *name_unique_cstr = name.GetCString(); + + static ConstString id_name("id"); + static ConstString Class_name("Class"); + + if (name == id_name || name == Class_name) + return; + + if (name_unique_cstr == NULL) + return; + + // The ClangASTSource is not responsible for finding $-names. + if (name_unique_cstr[0] == '$') + return; + + if (module_sp && namespace_decl) + { + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + + if (symbol_vendor) + { + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &namespace_decl); + + if (found_namespace_decl) + { + context.m_namespace_map->push_back(std::pair(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 + { + const ModuleList &target_images = m_target->GetImages(); + Mutex::Locker modules_locker (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; + + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = image->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &namespace_decl); + + if (found_namespace_decl) + { + context.m_namespace_map->push_back(std::pair(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 + { + TypeList types; + SymbolContext null_sc; + const bool exact_match = false; + + if (module_sp && namespace_decl) + module_sp->FindTypesInNamespace(null_sc, name, &namespace_decl, 1, types); + else + m_target->GetImages().FindTypes(null_sc, name, exact_match, 1, types); + + if (types.GetSize()) + { + lldb::TypeSP type_sp = types.GetTypeAtIndex(0); + + 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 : "")); + } + + ClangASTType full_type = type_sp->GetClangFullType(); + + ClangASTType copied_clang_type (GuardedCopyType(full_type)); + + if (!copied_clang_type) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type", + current_id); + + break; + } + + context.AddTypeDecl(copied_clang_type); + } + else + { + 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(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + break; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector types; + + if (!type_vendor->FindTypes(name, + append, + max_matches, + types)) + break; + + if (log) + { + log->Printf(" CAS::FEVD[%u] Matching type found for \"%s\" in the runtime", + current_id, + name.GetCString()); + } + + ClangASTType copied_clang_type (GuardedCopyType(types[0])); + + if (!copied_clang_type) + { + if (log) + log->Printf(" CAS::FEVD[%u] - Couldn't export a type from the runtime", + current_id); + + break; + } + + context.AddTypeDecl(copied_clang_type); + } + while(0); + } + + } while(0); +} + +template class TaggedASTDecl { +public: + TaggedASTDecl() : decl(NULL) { } + TaggedASTDecl(D *_decl) : decl(_decl) { } + bool IsValid() const { return (decl != NULL); } + bool IsInvalid() const { return !IsValid(); } + D *operator->() const { return decl; } + D *decl; +}; + +template class TD, class D1> +TD +DynCast(TD source) +{ + return TD (dyn_cast(source.decl)); +} + +template class DeclFromParser; +template class DeclFromUser; + +template class DeclFromParser : public TaggedASTDecl { +public: + DeclFromParser() : TaggedASTDecl() { } + DeclFromParser(D *_decl) : TaggedASTDecl(_decl) { } + + DeclFromUser GetOrigin(ClangASTImporter *importer); +}; + +template class DeclFromUser : public TaggedASTDecl { +public: + DeclFromUser() : TaggedASTDecl() { } + DeclFromUser(D *_decl) : TaggedASTDecl(_decl) { } + + DeclFromParser Import(ClangASTImporter *importer, ASTContext &dest_ctx); +}; + +template +DeclFromUser +DeclFromParser::GetOrigin(ClangASTImporter *importer) +{ + DeclFromUser <> origin_decl; + importer->ResolveDeclOrigin(this->decl, &origin_decl.decl, NULL); + if (origin_decl.IsInvalid()) + return DeclFromUser(); + return DeclFromUser(dyn_cast(origin_decl.decl)); +} + +template +DeclFromParser +DeclFromUser::Import(ClangASTImporter *importer, ASTContext &dest_ctx) +{ + DeclFromParser <> parser_generic_decl(importer->CopyDecl(&dest_ctx, &this->decl->getASTContext(), this->decl)); + if (parser_generic_decl.IsInvalid()) + return DeclFromParser(); + return DeclFromParser(dyn_cast(parser_generic_decl.decl)); +} + +static bool +FindObjCMethodDeclsWithOrigin (unsigned int current_id, + NameSearchContext &context, + ObjCInterfaceDecl *original_interface_decl, + clang::ASTContext *ast_context, + ClangASTImporter *ast_importer, + 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.c_str()); + original_selector = original_ctx->Selectors.getSelector(1, &ident); + } + else + { + SmallVector 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); + + ObjCInterfaceDecl::lookup_result result = original_interface_decl->lookup(original_decl_name); + + if (result.empty()) + return false; + + if (!result[0]) + return false; + + ObjCMethodDecl *result_method = dyn_cast(result[0]); + + if (!result_method) + return false; + + Decl *copied_decl = ast_importer->CopyDecl(ast_context, &result_method->getASTContext(), result_method); + + if (!copied_decl) + return false; + + ObjCMethodDecl *copied_method_decl = dyn_cast(copied_decl); + + if (!copied_method_decl) + return false; + + 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)); + + 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(decl_ctx); + + if (!interface_decl) + return; + + do + { + Decl *original_decl = NULL; + ASTContext *original_ctx = NULL; + + m_ast_importer->ResolveDeclOrigin(interface_decl, &original_decl, &original_ctx); + + if (!original_decl) + break; + + ObjCInterfaceDecl *original_interface_decl = dyn_cast(original_decl); + + if (FindObjCMethodDeclsWithOrigin(current_id, + context, + original_interface_decl, + m_ast_context, + m_ast_importer, + "at origin")) + return; // found it, no need to look any further + } while (0); + + 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(); + + ConstString selector_name(ss.GetData()); + + if (log) + log->Printf("ClangASTSource::FindObjCMethodDecls[%d] on (ASTContext*)%p for selector [%s %s]", + current_id, + 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.GetData()); + + 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.GetData()); + + 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 (0); + + 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; + + DeclContext *function_ctx = sc.function->GetClangDeclContext(); + + if (!function_ctx) + continue; + + ObjCMethodDecl *method_decl = dyn_cast(function_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 = m_ast_importer->CopyDecl(m_ast_context, &method_decl->getASTContext(), method_decl); + + if (!copied_decl) + continue; + + ObjCMethodDecl *copied_method_decl = dyn_cast(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(interface_decl)); + + if (!complete_interface_decl) + break; + + // We found the complete interface. The runtime never needs to be queried in this scenario. + + DeclFromUser 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, + complete_interface_decl, + &complete_iface_decl->getASTContext()); + + FindObjCMethodDeclsWithOrigin(current_id, + context, + complete_interface_decl, + m_ast_context, + m_ast_importer, + "in debug info"); + + return; + } + while (0); + + do + { + // Check the runtime only if the debug information didn't have a complete interface. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + break; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + break; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + ConstString interface_name(interface_decl->getNameAsString().c_str()); + bool append = false; + uint32_t max_matches = 1; + std::vector types; + + if (!type_vendor->FindTypes(interface_name, + append, + max_matches, + types)) + break; + + const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); + + const ObjCInterfaceType *runtime_interface_type = dyn_cast(runtime_clang_type); + + if (!runtime_interface_type) + break; + + ObjCInterfaceDecl *runtime_interface_decl = runtime_interface_type->getDecl(); + + FindObjCMethodDeclsWithOrigin(current_id, + context, + runtime_interface_decl, + m_ast_context, + m_ast_importer, + "in runtime"); + } + while(0); +} + +static bool +FindObjCPropertyAndIvarDeclsWithOrigin (unsigned int current_id, + NameSearchContext &context, + clang::ASTContext &ast_context, + ClangASTImporter *ast_importer, + DeclFromUser &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.c_str()); + IdentifierInfo &name_identifier(origin_iface_decl->getASTContext().Idents.get(name)); + + DeclFromUser origin_property_decl(origin_iface_decl->FindPropertyDeclaration(&name_identifier)); + + bool found = false; + + if (origin_property_decl.IsValid()) + { + DeclFromParser parser_property_decl(origin_property_decl.Import(ast_importer, ast_context)); + 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 origin_ivar_decl(origin_iface_decl->getIvarDecl(&name_identifier)); + + if (origin_ivar_decl.IsValid()) + { + DeclFromParser parser_ivar_decl(origin_ivar_decl.Import(ast_importer, ast_context)); + 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 parser_iface_decl(cast(context.m_decl_context)); + DeclFromUser origin_iface_decl(parser_iface_decl.GetOrigin(m_ast_importer)); + + ConstString class_name(parser_iface_decl->getNameAsString().c_str()); + + if (log) + log->Printf("ClangASTSource::FindObjCPropertyAndIvarDecls[%d] on (ASTContext*)%p for '%s.%s'", + current_id, + m_ast_context, + parser_iface_decl->getNameAsString().c_str(), + context.m_decl_name.getAsString().c_str()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + 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, + origin_iface_decl.decl, + &origin_iface_decl->getASTContext()); + + SymbolContext null_sc; + TypeList type_list; + + do + { + ObjCInterfaceDecl *complete_interface_decl = GetCompleteObjCInterface(const_cast(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 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, + complete_iface_decl.decl, + &complete_iface_decl->getASTContext()); + + FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + complete_iface_decl); + + return; + } + while(0); + + do + { + // Check the runtime only if the debug information didn't have a complete interface. + + lldb::ProcessSP process(m_target->GetProcessSP()); + + if (!process) + return; + + ObjCLanguageRuntime *language_runtime(process->GetObjCLanguageRuntime()); + + if (!language_runtime) + return; + + TypeVendor *type_vendor = language_runtime->GetTypeVendor(); + + if (!type_vendor) + break; + + bool append = false; + uint32_t max_matches = 1; + std::vector types; + + if (!type_vendor->FindTypes(class_name, + append, + max_matches, + types)) + break; + + const clang::Type *runtime_clang_type = QualType::getFromOpaquePtr(types[0].GetOpaqueQualType()).getTypePtr(); + + const ObjCInterfaceType *runtime_interface_type = dyn_cast(runtime_clang_type); + + if (!runtime_interface_type) + break; + + DeclFromUser runtime_iface_decl(runtime_interface_type->getDecl()); + + if (log) + log->Printf("CAS::FOPD[%d] trying runtime (ObjCInterfaceDecl*)%p/(ASTContext*)%p...", + current_id, + runtime_iface_decl.decl, + &runtime_iface_decl->getASTContext()); + + if (FindObjCPropertyAndIvarDeclsWithOrigin(current_id, + context, + *m_ast_context, + m_ast_importer, + runtime_iface_decl)) + return; + } + while(0); +} + +typedef llvm::DenseMap FieldOffsetMap; +typedef llvm::DenseMap BaseOffsetMap; + +template +static bool +ImportOffsetMap (llvm::DenseMap &destination_map, + llvm::DenseMap &source_map, + ClangASTImporter *importer, + ASTContext &dest_ctx) +{ + typedef llvm::DenseMap MapType; + + for (typename MapType::iterator fi = source_map.begin(), fe = source_map.end(); + fi != fe; + ++fi) + { + DeclFromUser user_decl(const_cast(fi->first)); + DeclFromParser parser_decl(user_decl.Import(importer, dest_ctx)); + if (parser_decl.IsInvalid()) + return false; + destination_map.insert(std::pair(parser_decl.decl, fi->second)); + } + + return true; +} + +template bool ExtractBaseOffsets (const ASTRecordLayout &record_layout, + DeclFromUser &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(); + + if (!origin_base_record_type) + return false; + + DeclFromUser origin_base_record(origin_base_record_type->getDecl()); + + if (origin_base_record.IsInvalid()) + return false; + + DeclFromUser origin_base_cxx_record(DynCast(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(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, + m_ast_context, + record, + record->getNameAsString().c_str()); + } + + + DeclFromParser parser_record(record); + DeclFromUser origin_record(parser_record.GetOrigin(m_ast_importer)); + + 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(origin_record.decl)); + + if (!origin_record.decl->getDefinition()) + 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(*fi, field_offset)); + + field_idx++; + } + + ASTContext &parser_ast_context(record->getASTContext()); + + DeclFromUser origin_cxx_record(DynCast(origin_record)); + + if (origin_cxx_record.IsValid()) + { + if (!ExtractBaseOffsets(record_layout, origin_cxx_record, origin_base_offsets) || + !ExtractBaseOffsets(record_layout, origin_cxx_record, origin_virtual_base_offsets)) + return false; + } + + if (!ImportOffsetMap(field_offsets, origin_field_offsets, m_ast_importer, parser_ast_context) || + !ImportOffsetMap(base_offsets, origin_base_offsets, m_ast_importer, parser_ast_context) || + !ImportOffsetMap(virtual_base_offsets, origin_virtual_base_offsets, m_ast_importer, parser_ast_context)) + 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, 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, + *fi, + fi->getNameAsString().c_str(), + field_offsets[*fi]); + } + DeclFromParser parser_cxx_record = DynCast(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(); + DeclFromParser base_record(base_record_type->getDecl()); + DeclFromParser base_cxx_record = DynCast(base_record); + + log->Printf("LRT[%u] %s(CXXRecordDecl*)%p, Name = '%s', Offset = %" PRId64 " chars", + current_id, + (is_virtual ? "Virtual " : ""), + 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, + const 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, + m_ast_context, + name.GetCString(), + parent_map->begin()->second.GetNamespaceDecl()->getDeclName().getAsString().c_str()); + else + log->Printf("CompleteNamespaceMap[%u] on (ASTContext*)%p Searching for namespace %s", + current_id, + m_ast_context, + name.GetCString()); + } + + + if (parent_map) + { + for (ClangASTImporter::NamespaceMap::iterator i = parent_map->begin(), e = parent_map->end(); + i != e; + ++i) + { + ClangNamespaceDecl found_namespace_decl; + + lldb::ModuleSP module_sp = i->first; + ClangNamespaceDecl module_parent_namespace_decl = i->second; + + SymbolVendor *symbol_vendor = module_sp->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &module_parent_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair(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(); + Mutex::Locker modules_locker(target_images.GetMutex()); + + ClangNamespaceDecl 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; + + ClangNamespaceDecl found_namespace_decl; + + SymbolVendor *symbol_vendor = image->GetSymbolVendor(); + + if (!symbol_vendor) + continue; + + SymbolContext null_sc; + + found_namespace_decl = symbol_vendor->FindNamespace(null_sc, name, &null_namespace_decl); + + if (!found_namespace_decl) + continue; + + namespace_map->push_back(std::pair(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 NULL; + + const ClangNamespaceDecl &namespace_decl = namespace_decls->begin()->second; + + Decl *copied_decl = m_ast_importer->CopyDecl(m_ast_context, namespace_decl.GetASTContext(), namespace_decl.GetNamespaceDecl()); + + if (!copied_decl) + return NULL; + + NamespaceDecl *copied_namespace_decl = dyn_cast(copied_decl); + + if (!copied_namespace_decl) + return NULL; + + context.m_decls.push_back(copied_namespace_decl); + + m_ast_importer->RegisterNamespaceMap(copied_namespace_decl, namespace_decls); + + return dyn_cast(copied_decl); +} + +ClangASTType +ClangASTSource::GuardedCopyType (const ClangASTType &src_type) +{ + ClangASTMetrics::RegisterLLDBImport(); + + SetImportInProgress(true); + + QualType copied_qual_type = m_ast_importer->CopyType (m_ast_context, src_type.GetASTContext(), src_type.GetQualType()); + + 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 ClangASTType(); + + return ClangASTType(m_ast_context, copied_qual_type); +} + +clang::NamedDecl * +NameSearchContext::AddVarDecl(const ClangASTType &type) +{ + assert (type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return NULL; + + IdentifierInfo *ii = m_decl_name.getAsIdentifierInfo(); + + clang::ASTContext *ast = type.GetASTContext(); + + clang::NamedDecl *Decl = VarDecl::Create(*ast, + const_cast(m_decl_context), + SourceLocation(), + SourceLocation(), + ii, + type.GetQualType(), + 0, + SC_Static); + m_decls.push_back(Decl); + + return Decl; +} + +clang::NamedDecl * +NameSearchContext::AddFunDecl (const ClangASTType &type) +{ + assert (type && "Type for variable must be valid!"); + + if (!type.IsValid()) + return NULL; + + if (m_function_types.count(type)) + return NULL; + + m_function_types.insert(type); + + QualType qual_type (type.GetQualType()); + + clang::ASTContext *ast = type.GetASTContext(); + + const bool isInlineSpecified = false; + const bool hasWrittenPrototype = true; + const bool isConstexprSpecified = false; + + clang::FunctionDecl *func_decl = FunctionDecl::Create (*ast, + const_cast(m_decl_context), + SourceLocation(), + SourceLocation(), + m_decl_name.getAsIdentifierInfo(), + qual_type, + NULL, + SC_Static, + isInlineSpecified, + hasWrittenPrototype, + isConstexprSpecified); + + // 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(); + + if (func_proto_type) + { + unsigned NumArgs = func_proto_type->getNumArgs(); + unsigned ArgIndex; + + SmallVector parm_var_decls; + + for (ArgIndex = 0; ArgIndex < NumArgs; ++ArgIndex) + { + QualType arg_qual_type (func_proto_type->getArgType(ArgIndex)); + + parm_var_decls.push_back(ParmVarDecl::Create (*ast, + const_cast(m_decl_context), + SourceLocation(), + SourceLocation(), + NULL, + arg_qual_type, + NULL, + SC_Static, + NULL)); + } + + func_decl->setParams(ArrayRef(parm_var_decls)); + } + else + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("Function type wasn't a FunctionProtoType"); + } + + 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(), // argument types + proto_info)); + + return AddFunDecl(ClangASTType (m_ast_source.m_ast_context, generic_function_type)); +} + +clang::NamedDecl * +NameSearchContext::AddTypeDecl(const ClangASTType &clang_type) +{ + if (clang_type) + { + QualType qual_type = clang_type.GetQualType(); + + if (const TypedefType *typedef_type = llvm::dyn_cast(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()) + { + 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()) + { + ObjCInterfaceDecl *interface_decl = objc_object_type->getInterface(); + + m_decls.push_back((NamedDecl*)interface_decl); + + return (NamedDecl*)interface_decl; + } + } + return NULL; +} + +void +NameSearchContext::AddLookupResult (clang::DeclContextLookupConstResult 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/source/Expression/ClangExpressionDeclMap.cpp b/source/Expression/ClangExpressionDeclMap.cpp new file mode 100644 index 00000000000..072e7819cd0 --- /dev/null +++ b/source/Expression/ClangExpressionDeclMap.cpp @@ -0,0 +1,1956 @@ +//===-- ClangExpressionDeclMap.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangExpressionDeclMap.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/Decl.h" +#include "lldb/lldb-private.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ASTDumper.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangNamespaceDecl.h" +#include "lldb/Symbol/CompileUnit.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Symbol/SymbolContext.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/ObjCLanguageRuntime.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" + +using namespace lldb; +using namespace lldb_private; +using namespace clang; + +ClangExpressionDeclMap::ClangExpressionDeclMap (bool keep_result_in_memory, ExecutionContext &exe_ctx) : + ClangASTSource (exe_ctx.GetTargetSP()), + m_found_entities (), + m_struct_members (), + m_keep_result_in_memory (keep_result_in_memory), + 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 = &target->GetPersistentVariables(); + + if (!target->GetScratchClangASTContext()) + return false; + } + + m_parser_vars->m_target_info = GetTargetInfo(); + m_parser_vars->m_materializer = materializer; + + return true; +} + +void +ClangExpressionDeclMap::DidParse() +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + ClangASTMetrics::DumpCounters(log); + + if (m_parser_vars.get()) + { + for (size_t entity_index = 0, num_entities = m_found_entities.GetSize(); + entity_index < num_entities; + ++entity_index) + { + ClangExpressionVariableSP var_sp(m_found_entities.GetVariableAtIndex(entity_index)); + if (var_sp) + var_sp->DisableParserVars(GetParserID()); + } + + for (size_t pvar_index = 0, num_pvars = m_parser_vars->m_persistent_vars->GetSize(); + pvar_index < num_pvars; + ++pvar_index) + { + ClangExpressionVariableSP pvar_sp(m_parser_vars->m_persistent_vars->GetVariableAtIndex(pvar_index)); + if (pvar_sp) + pvar_sp->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; +} + +bool +ClangExpressionDeclMap::AddPersistentVariable +( + const NamedDecl *decl, + const ConstString &name, + TypeFromParser parser_type, + bool is_result, + bool is_lvalue +) +{ + assert (m_parser_vars.get()); + + if (m_parser_vars->m_materializer && is_result) + { + Error err; + + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + if (target == NULL) + return false; + + ASTContext *context(target->GetScratchClangASTContext()->getASTContext()); + + TypeFromUser user_type(m_ast_importer->DeportType(context, + parser_type.GetASTContext(), + parser_type.GetOpaqueQualType()), + context); + + uint32_t offset = m_parser_vars->m_materializer->AddResultVariable(user_type, is_lvalue, m_keep_result_in_memory, err); + + ClangExpressionVariableSP var_sp = m_found_entities.CreateVariable(exe_ctx.GetBestExecutionContextScope(), + name, + user_type, + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size); + + if (!var_sp) + return false; + + var_sp->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + var_sp->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var_sp->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 == NULL) + return false; + + ASTContext *context(target->GetScratchClangASTContext()->getASTContext()); + + TypeFromUser user_type(m_ast_importer->DeportType(context, + parser_type.GetASTContext(), + parser_type.GetOpaqueQualType()), + context); + + 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; + + ClangExpressionVariableSP var_sp = 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); + + if (!var_sp) + return false; + + var_sp->m_frozen_sp->SetHasCompleteType(); + + if (is_result) + var_sp->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + else + var_sp->m_flags |= ClangExpressionVariable::EVKeepInTarget; // explicitly-declared persistent variables should persist + + if (is_lvalue) + { + var_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference; + } + else + { + var_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + var_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + } + + if (m_keep_result_in_memory) + { + var_sp->m_flags |= ClangExpressionVariable::EVKeepInTarget; + } + + if (log) + log->Printf("Created persistent variable with flags 0x%hx", var_sp->m_flags); + + var_sp->EnableParserVars(GetParserID()); + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_named_decl = decl; + parser_vars->m_parser_type = parser_type; + + return true; +} + +bool +ClangExpressionDeclMap::AddValueToStruct +( + const NamedDecl *decl, + const ConstString &name, + llvm::Value *value, + size_t size, + off_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 (m_struct_members.GetVariable(decl, GetParserID())) + return true; + + ClangExpressionVariableSP var_sp (m_found_entities.GetVariable(decl, GetParserID())); + + if (!var_sp) + { + var_sp = m_parser_vars->m_persistent_vars->GetVariable(decl, GetParserID()); + is_persistent_variable = true; + } + + if (!var_sp) + return false; + + if (log) + log->Printf("Adding value for (NamedDecl*)%p [%s - %s] to the structure", + decl, + name.GetCString(), + var_sp->GetName().GetCString()); + + // We know entity->m_parser_vars is valid because we used a parser variable + // to find it + + ClangExpressionVariable::ParserVars *parser_vars = var_sp->GetParserVars(GetParserID()); + + parser_vars->m_llvm_value = value; + + if (ClangExpressionVariable::JITVars *jit_vars = var_sp->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); + } + + var_sp->EnableJITVars(GetParserID()); + + ClangExpressionVariable::JITVars *jit_vars = var_sp->GetJITVars(GetParserID()); + + jit_vars->m_alignment = alignment; + jit_vars->m_size = size; + + m_struct_members.AddVariable(var_sp); + + if (m_parser_vars->m_materializer) + { + uint32_t offset = 0; + + Error err; + + if (is_persistent_variable) + { + offset = m_parser_vars->m_materializer->AddPersistentVariable(var_sp, 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_sp->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, + off_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, + off_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; + + ClangExpressionVariableSP member_sp(m_struct_members.GetVariableAtIndex(index)); + + if (!member_sp) + return false; + + ClangExpressionVariable::ParserVars *parser_vars = member_sp->GetParserVars(GetParserID()); + ClangExpressionVariable::JITVars *jit_vars = member_sp->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 +) +{ + ClangExpressionVariableSP entity_sp(m_found_entities.GetVariable(decl, GetParserID())); + + if (!entity_sp) + return false; + + // We know m_parser_vars is valid since we searched for the variable by + // its NamedDecl + + ClangExpressionVariable::ParserVars *parser_vars = entity_sp->GetParserVars(GetParserID()); + + ptr = parser_vars->m_lldb_value.GetScalar().ULongLong(); + + return true; +} + +static void +FindCodeSymbolInContext +( + const ConstString &name, + SymbolContext &sym_ctx, + SymbolContextList &sc_list +) +{ + SymbolContextList temp_sc_list; + if (sym_ctx.module_sp) + sym_ctx.module_sp->FindSymbolsWithNameAndType(name, eSymbolTypeAny, temp_sc_list); + + if (!sc_list.GetSize() && sym_ctx.target_sp) + sym_ctx.target_sp->GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, temp_sc_list); + + unsigned temp_sc_list_size = temp_sc_list.GetSize(); + for (unsigned i = 0; i < temp_sc_list_size; i++) + { + SymbolContext sym_ctx; + temp_sc_list.GetContextAtIndex(i, sym_ctx); + if (sym_ctx.symbol) + { + switch (sym_ctx.symbol->GetType()) + { + case eSymbolTypeCode: + case eSymbolTypeResolver: + sc_list.Append(sym_ctx); + break; + + default: + break; + } + } + } +} + +bool +ClangExpressionDeclMap::GetFunctionAddress +( + const ConstString &name, + uint64_t &func_addr +) +{ + assert (m_parser_vars.get()); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + ExecutionContext &exe_ctx = m_parser_vars->m_exe_ctx; + Target *target = exe_ctx.GetTargetPtr(); + // Back out in all cases where we're not fully initialized + if (target == NULL) + return false; + if (!m_parser_vars->m_sym_ctx.target_sp) + return false; + + SymbolContextList sc_list; + + FindCodeSymbolInContext(name, m_parser_vars->m_sym_ctx, sc_list); + + if (!sc_list.GetSize()) + { + // We occasionally get debug information in which a const function is reported + // as non-const, so the mangled name is wrong. This is a hack to compensate. + + if (!strncmp(name.GetCString(), "_ZN", 3) && + strncmp(name.GetCString(), "_ZNK", 4)) + { + std::string fixed_scratch("_ZNK"); + fixed_scratch.append(name.GetCString() + 3); + ConstString fixed_name(fixed_scratch.c_str()); + + if (log) + log->Printf("Failed to find symbols given non-const name %s; trying %s", name.GetCString(), fixed_name.GetCString()); + + FindCodeSymbolInContext(fixed_name, m_parser_vars->m_sym_ctx, sc_list); + } + } + + if (!sc_list.GetSize()) + return false; + + SymbolContext sym_ctx; + sc_list.GetContextAtIndex(0, sym_ctx); + + const Address *func_so_addr = NULL; + bool is_indirect_function = false; + + if (sym_ctx.function) + func_so_addr = &sym_ctx.function->GetAddressRange().GetBaseAddress(); + else if (sym_ctx.symbol) { + func_so_addr = &sym_ctx.symbol->GetAddress(); + is_indirect_function = sym_ctx.symbol->IsIndirect(); + } else + return false; + + if (!func_so_addr || !func_so_addr->IsValid()) + return false; + + func_addr = func_so_addr->GetCallableLoadAddress (target, is_indirect_function); + + return true; +} + +addr_t +ClangExpressionDeclMap::GetSymbolAddress (Target &target, Process *process, const ConstString &name, lldb::SymbolType symbol_type) +{ + SymbolContextList sc_list; + + 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; iGetAddress(); + + if (!sym_address || !sym_address->IsValid()) + continue; + + if (sym_address) + { + 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 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 = process->GetObjCLanguageRuntime(); + + if (runtime) + { + symbol_load_addr = runtime->LookupRuntimeSymbol(name); + } + } + + return symbol_load_addr; +} + +addr_t +ClangExpressionDeclMap::GetSymbolAddress (const 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); +} + +const Symbol * +ClangExpressionDeclMap::FindGlobalDataSymbol (Target &target, + const ConstString &name) +{ + SymbolContextList sc_list; + + target.GetImages().FindSymbolsWithNameAndType(name, eSymbolTypeAny, sc_list); + + const uint32_t matches = sc_list.GetSize(); + for (uint32_t i=0; iGetAddress(); + + if (sym_address && 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; + } + return symbol; + + 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; + } + } + } + } + + return NULL; +} + +lldb::VariableSP +ClangExpressionDeclMap::FindGlobalVariable +( + Target &target, + ModuleSP &module, + const ConstString &name, + ClangNamespaceDecl *namespace_decl, + TypeFromUser *type +) +{ + VariableList vars; + + if (module && namespace_decl) + module->FindGlobalVariables (name, namespace_decl, true, -1, vars); + else + target.GetImages().FindGlobalVariables(name, true, -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()->GetClangFullType())) + return var_sp; + } + } + else + { + return vars.GetVariableAtIndex(0); + } + } + + return VariableSP(); +} + +// 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(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(context.m_decl_context)) + { + ClangASTImporter::NamespaceMapSP namespace_map = m_ast_importer->GetNamespaceMap(namespace_context); + + if (log && log->GetVerbose()) + log->Printf(" CEDM::FEVD[%u] Inspecting (NamespaceMap*)%p (%d entries)", + current_id, + namespace_map.get(), + (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(" CEDM::FEVD[%u] Searching namespace %s in module %s", + current_id, + i->second.GetNamespaceDecl()->getNameAsString().c_str(), + i->first->GetFileSpec().GetFilename().GetCString()); + + FindExternalVisibleDecls(context, + i->first, + i->second, + current_id); + } + } + else if (isa(context.m_decl_context)) + { + ClangNamespaceDecl namespace_decl; + + if (log) + log->Printf(" CEDM::FEVD[%u] Searching the root namespace", current_id); + + FindExternalVisibleDecls(context, + lldb::ModuleSP(), + namespace_decl, + current_id); + } + + if (!context.m_found.variable) + ClangASTSource::FindExternalVisibleDecls(context); +} + +void +ClangExpressionDeclMap::FindExternalVisibleDecls (NameSearchContext &context, + lldb::ModuleSP module_sp, + ClangNamespaceDecl &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()); + + const char *name_unique_cstr = name.GetCString(); + + if (name_unique_cstr == NULL) + return; + + static ConstString id_name("id"); + static ConstString Class_name("Class"); + + if (name == id_name || name == Class_name) + 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(); + if (name_unique_cstr[0] == '$' && !namespace_decl) + { + static ConstString g_lldb_class_name ("$__lldb_class"); + + if (name == g_lldb_class_name) + { + // Clang is looking for the type of "this" + + if (frame == NULL) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction); + + if (!sym_ctx.function) + return; + + // Get the block that defines the function + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + return; + + clang::CXXMethodDecl *method_decl = llvm::dyn_cast(decl_context); + + 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(), + &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()); + } + + TypeFromParser class_type = CopyClassType(class_user_type, current_id); + + if (!class_type.IsValid()) + return; + + TypeSourceInfo *type_source_info = m_ast_context->getTrivialTypeSourceInfo(QualType::getFromOpaquePtr(class_type.GetOpaqueQualType())); + + if (!type_source_info) + return; + + 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); + + 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(), + &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 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; + + ClangASTType pointee_type = this_type->GetClangForwardType().GetPointeeType(); + + if (pointee_type.IsValid()) + { + if (log) + { + ASTDumper ast_dumper(this_type->GetClangFullType()); + log->Printf(" FEVD[%u] Adding type for $__lldb_objc_class: %s", current_id, ast_dumper.GetCString()); + } + + TypeFromUser class_user_type(pointee_type); + AddOneType(context, class_user_type, current_id); + + + TypeFromUser this_user_type(this_type->GetClangFullType()); + 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) + { + // Clang is looking for the type of "*self" + + if (!frame) + return; + + SymbolContext sym_ctx = frame->GetSymbolContext(lldb::eSymbolContextFunction); + + if (!sym_ctx.function) + return; + + // Get the block that defines the function + Block *function_block = sym_ctx.GetFunctionBlock(); + + if (!function_block) + return; + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + return; + + clang::ObjCMethodDecl *method_decl = llvm::dyn_cast(decl_context); + + 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(), + &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(), + &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(), + &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 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; + + ClangASTType self_clang_type = self_type->GetClangFullType(); + + if (self_clang_type.IsObjCClassType()) + { + return; + } + else if (self_clang_type.IsObjCObjectPointerType()) + { + self_clang_type = self_clang_type.GetPointeeType(); + + if (!self_clang_type) + return; + + if (log) + { + ASTDumper ast_dumper(self_type->GetClangFullType()); + 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->GetClangFullType()); + + m_struct_vars->m_object_pointer_type = self_user_type; + return; + } + } + } + + return; + } + + // any other $__lldb names should be weeded out now + if (!::strncmp(name_unique_cstr, "$__lldb", sizeof("$__lldb") - 1)) + return; + + 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; + + TypeDecl *ptype_type_decl = m_parser_vars->m_persistent_vars->GetPersistentType(name); + + if (!ptype_type_decl) + break; + + Decl *parser_ptype_decl = m_ast_importer->CopyDecl(m_ast_context, scratch_ast_context, ptype_type_decl); + + if (!parser_ptype_decl) + break; + + TypeDecl *parser_ptype_type_decl = dyn_cast(parser_ptype_decl); + + if (!parser_ptype_type_decl) + break; + + if (log) + log->Printf(" CEDM::FEVD[%u] Found persistent type %s", current_id, name.GetCString()); + + context.AddNamedDecl(parser_ptype_type_decl); + } while (0); + + ClangExpressionVariableSP 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; + Error err; + + if (frame && !namespace_decl) + { + valobj = frame->GetValueForVariableExpressionPath(name_unique_cstr, + eNoDynamicValues, + StackFrame::eExpressionPathOptionCheckPtrVsMember || + StackFrame::eExpressionPathOptionsAllowDirectIVarAccess || + StackFrame::eExpressionPathOptionsNoFragileObjcIvar || + StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var, + err); + + // If we found a variable in scope, no need to pull up function names + if (err.Success() && var) + { + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + if (target) + { + var = FindGlobalVariable (*target, + module_sp, + name, + &namespace_decl, + NULL); + + if (var) + { + valobj = ValueObjectVariable::Create(target, var); + AddOneVariable(context, var, valobj, current_id); + context.m_found.variable = true; + return; + } + } + + if (!context.m_found.variable) + { + 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 (sc_list.GetSize()) + { + Symbol *generic_symbol = NULL; + Symbol *non_extern_symbol = NULL; + + 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) + { + clang::DeclContext *decl_ctx = sym_ctx.function->GetClangDeclContext(); + + if (!decl_ctx) + continue; + + // Filter out class/instance methods. + if (dyn_cast(decl_ctx)) + continue; + if (dyn_cast(decl_ctx)) + continue; + + AddOneFunction(context, sym_ctx.function, NULL, current_id); + context.m_found.function_with_type_info = true; + context.m_found.function = true; + } + else if (sym_ctx.symbol) + { + if (sym_ctx.symbol->IsExternal()) + generic_symbol = sym_ctx.symbol; + else + non_extern_symbol = sym_ctx.symbol; + } + } + + if (!context.m_found.function_with_type_info) + { + if (generic_symbol) + { + AddOneFunction (context, NULL, generic_symbol, current_id); + context.m_found.function = true; + } + else if (non_extern_symbol) + { + AddOneFunction (context, NULL, non_extern_symbol, current_id); + context.m_found.function = true; + } + } + } + + 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. + + const Symbol *data_symbol = FindGlobalDataSymbol(*target, name); + + if (data_symbol) + { + AddOneGenericVariable(context, *data_symbol, current_id); + context.m_found.variable = true; + } + } + } + } +} + +static clang_type_t +MaybePromoteToBlockPointerType +( + ASTContext *ast_context, + clang_type_t candidate_type +) +{ + if (!candidate_type) + return candidate_type; + + QualType candidate_qual_type = QualType::getFromOpaquePtr(candidate_type); + + const PointerType *candidate_pointer_type = dyn_cast(candidate_qual_type); + + if (!candidate_pointer_type) + return candidate_type; + + QualType pointee_qual_type = candidate_pointer_type->getPointeeType(); + + const RecordType *pointee_record_type = dyn_cast(pointee_qual_type); + + if (!pointee_record_type) + return candidate_type; + + RecordDecl *pointee_record_decl = pointee_record_type->getDecl(); + + if (!pointee_record_decl->isRecord()) + return candidate_type; + + if (!pointee_record_decl->getName().startswith(llvm::StringRef("__block_literal_"))) + return candidate_type; + + QualType generic_function_type = ast_context->getFunctionNoProtoType(ast_context->UnknownAnyTy); + QualType block_pointer_type = ast_context->getBlockPointerType(generic_function_type); + + return block_pointer_type.getAsOpaquePtr(); +} + +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; + } + + ClangASTType var_clang_type = var_type->GetClangFullType(); + + if (!var_clang_type) + { + if (log) + log->PutCString("Skipped a definition because it has no Clang type"); + return false; + } + + // commented out because of + ASTContext *ast = var_type->GetClangASTContext().getASTContext(); + + if (!ast) + { + if (log) + log->PutCString("There is no AST context for the current execution context"); + return false; + } + //var_clang_type = MaybePromoteToBlockPointerType (ast, var_clang_type); + + DWARFExpression &var_location_expr = var->LocationExpression(); + + lldb::addr_t loclist_base_load_addr = LLDB_INVALID_ADDRESS; + + Target *target = m_parser_vars->m_exe_ctx.GetTargetPtr(); + + if (var_location_expr.IsLocationList()) + { + SymbolContext var_sc; + var->CalculateSymbolContext (&var_sc); + loclist_base_load_addr = var_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (target); + } + Error 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; + } + } + + ClangASTType 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.SetClangType(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(parser_type)) + CompleteType(tag_type->getDecl()); + } + + + bool is_reference = pt.IsReferenceType(); + + NamedDecl *var_decl = NULL; + 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()); + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (valobj)); + + assert (entity.get()); + 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 = NULL; + 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, + ClangExpressionVariableSP &pvar_sp, + unsigned int current_id) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + TypeFromUser user_type (pvar_sp->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()); + + pvar_sp->EnableParserVars(GetParserID()); + ClangExpressionVariable::ParserVars *parser_vars = pvar_sp->GetParserVars(GetParserID()); + parser_vars->m_parser_type = parser_type; + parser_vars->m_named_decl = var_decl; + parser_vars->m_llvm_value = NULL; + 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 == NULL) + 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()); + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (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)); + assert (entity.get()); + + 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.SetClangType(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 = NULL; + 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(); + + ASTContext *scratch_ast_context = target->GetScratchClangASTContext()->getASTContext(); + + for (size_t index = 0, num_entities = m_found_entities.GetSize(); + index < num_entities; + ++index) + { + ClangExpressionVariableSP entity = m_found_entities.GetVariableAtIndex(index); + + ClangExpressionVariable::ParserVars *parser_vars = entity->GetParserVars(GetParserID()); + + if (entity->m_flags & ClangExpressionVariable::EVUnknownType) + { + const NamedDecl *named_decl = parser_vars->m_named_decl; + const VarDecl *var_decl = dyn_cast(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(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(), &var_decl->getASTContext()); + + lldb::clang_type_t copied_type = m_ast_importer->CopyType(scratch_ast_context, &var_decl->getASTContext(), var_type.getAsOpaquePtr()); + + if (!copied_type) + { + if (log) + log->Printf("ClangExpressionDeclMap::ResolveUnknownType - Couldn't import the type for a variable"); + + return (bool) lldb::ClangExpressionVariableSP(); + } + + TypeFromUser user_type(copied_type, scratch_ast_context); + +// parser_vars->m_lldb_value.SetContext(Value::eContextTypeClangType, user_type.GetOpaqueQualType()); + parser_vars->m_lldb_value.SetClangType(user_type); + parser_vars->m_parser_type = parser_type; + + entity->SetClangType(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)); + + ClangASTType 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); + + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope(), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + 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 = NULL; + 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 = NULL; + const Address *fun_address = NULL; + ClangASTType function_clang_type; + + bool is_indirect_function = false; + + if (function) + { + Type *function_type = function->GetType(); + + if (!function_type) + { + if (log) + log->PutCString(" Skipped a function because it has no type"); + return; + } + + function_clang_type = function_type->GetClangFullType(); + + if (!function_clang_type) + { + if (log) + log->PutCString(" Skipped a function because it has no Clang type"); + return; + } + + fun_address = &function->GetAddressRange().GetBaseAddress(); + + ClangASTType copied_function_type = GuardedCopyType(function_clang_type); + if (copied_function_type) + { + function_decl = context.AddFunDecl(copied_function_type); + + 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); + + ClangExpressionVariableSP entity(m_found_entities.CreateVariable (m_parser_vars->m_exe_ctx.GetBestExecutionContextScope (), + m_parser_vars->m_target_info.byte_order, + m_parser_vars->m_target_info.address_byte_size)); + assert (entity.get()); + + std::string decl_name(context.m_decl_name.getAsString()); + entity->SetName(ConstString(decl_name.c_str())); + entity->SetClangType (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 = NULL; + + if (log) + { + ASTDumper ast_dumper(function_decl); + + 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(), + ast_dumper.GetCString()); + } +} + +TypeFromParser +ClangExpressionDeclMap::CopyClassType(TypeFromUser &ut, + unsigned int current_id) +{ + ClangASTType copied_clang_type = GuardedCopyType(ut); + + if (!copied_clang_type) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + log->Printf("ClangExpressionDeclMap::CopyClassType - Couldn't import the type"); + + return TypeFromParser(); + } + + if (copied_clang_type.IsAggregateType() && copied_clang_type.GetCompleteType ()) + { + ClangASTType void_clang_type = ClangASTContext::GetBasicType(m_ast_context, eBasicTypeVoid); + ClangASTType void_ptr_clang_type = void_clang_type.GetPointerType(); + + ClangASTType method_type = ClangASTContext::CreateFunctionType (m_ast_context, + void_clang_type, + &void_ptr_clang_type, + 1, + false, + copied_clang_type.GetTypeQualifiers()); + + 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; + + copied_clang_type.AddMethodToCXXRecordType ("$__lldb_expr", + method_type, + lldb::eAccessPublic, + is_virtual, + is_static, + is_inline, + is_explicit, + is_attr_used, + is_artificial); + } + + return TypeFromParser(copied_clang_type); +} + +void +ClangExpressionDeclMap::AddOneType(NameSearchContext &context, + TypeFromUser &ut, + unsigned int current_id) +{ + ClangASTType 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/source/Expression/ClangExpressionParser.cpp b/source/Expression/ClangExpressionParser.cpp new file mode 100644 index 00000000000..98c0bfdca87 --- /dev/null +++ b/source/Expression/ClangExpressionParser.cpp @@ -0,0 +1,595 @@ +//===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Expression/ClangExpressionParser.h" + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangASTSource.h" +#include "lldb/Expression/ClangExpression.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRDynamicChecks.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "clang/AST/ASTContext.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/Version.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Driver/CC1Options.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/Frontend/FrontendActions.h" +#include "clang/Sema/SemaConsumer.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/TargetSelect.h" + +#if defined(__FreeBSD__) +#define USE_STANDARD_JIT +#endif + +#if defined (USE_STANDARD_JIT) +#include "llvm/ExecutionEngine/JIT.h" +#else +#include "llvm/ExecutionEngine/MCJIT.h" +#endif +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Signals.h" + +using namespace clang; +using namespace llvm; +using namespace lldb_private; + +//===----------------------------------------------------------------------===// +// Utility Methods for Clang +//===----------------------------------------------------------------------===// + +std::string GetBuiltinIncludePath(const char *Argv0) { + SmallString<128> P(llvm::sys::fs::getMainExecutable( + Argv0, (void *)(intptr_t) GetBuiltinIncludePath)); + + if (!P.empty()) { + llvm::sys::path::remove_filename(P); // Remove /clang from foo/bin/clang + llvm::sys::path::remove_filename(P); // Remove /bin from foo/bin + + // Get foo/lib/clang//include + llvm::sys::path::append(P, "lib", "clang", CLANG_VERSION_STRING, + "include"); + } + + return P.str(); +} + + +//===----------------------------------------------------------------------===// +// Main driver for Clang +//===----------------------------------------------------------------------===// + +static void LLVMErrorHandler(void *UserData, const std::string &Message) { + DiagnosticsEngine &Diags = *static_cast(UserData); + + Diags.Report(diag::err_fe_error_backend) << Message; + + // We cannot recover from llvm errors. + assert(0); +} + +static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { + using namespace clang::frontend; + + switch (CI.getFrontendOpts().ProgramAction) { + default: + llvm_unreachable("Invalid program action!"); + + case ASTDump: return new ASTDumpAction(); + case ASTPrint: return new ASTPrintAction(); + case ASTDumpXML: return new ASTDumpXMLAction(); + case ASTView: return new ASTViewAction(); + case DumpRawTokens: return new DumpRawTokensAction(); + case DumpTokens: return new DumpTokensAction(); + case EmitAssembly: return new EmitAssemblyAction(); + case EmitBC: return new EmitBCAction(); + case EmitHTML: return new HTMLPrintAction(); + case EmitLLVM: return new EmitLLVMAction(); + case EmitLLVMOnly: return new EmitLLVMOnlyAction(); + case EmitCodeGenOnly: return new EmitCodeGenOnlyAction(); + case EmitObj: return new EmitObjAction(); + case FixIt: return new FixItAction(); + case GeneratePCH: return new GeneratePCHAction(); + case GeneratePTH: return new GeneratePTHAction(); + case InitOnly: return new InitOnlyAction(); + case ParseSyntaxOnly: return new SyntaxOnlyAction(); + + case PluginAction: { + for (FrontendPluginRegistry::iterator it = + FrontendPluginRegistry::begin(), ie = FrontendPluginRegistry::end(); + it != ie; ++it) { + if (it->getName() == CI.getFrontendOpts().ActionName) { + llvm::OwningPtr P(it->instantiate()); + if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs)) + return 0; + return P.take(); + } + } + + CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) + << CI.getFrontendOpts().ActionName; + return 0; + } + + case PrintDeclContext: return new DeclContextPrintAction(); + case PrintPreamble: return new PrintPreambleAction(); + case PrintPreprocessedInput: return new PrintPreprocessedAction(); + case RewriteMacros: return new RewriteMacrosAction(); + case RewriteObjC: return new RewriteObjCAction(); + case RewriteTest: return new RewriteTestAction(); + //case RunAnalysis: return new AnalysisAction(); + case RunPreprocessorOnly: return new PreprocessOnlyAction(); + } +} + +static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { + // Create the underlying action. + FrontendAction *Act = CreateFrontendBaseAction(CI); + if (!Act) + return 0; + + // If there are any AST files to merge, create a frontend action + // adaptor to perform the merge. + if (!CI.getFrontendOpts().ASTMergeFiles.empty()) + Act = new ASTMergeAction(Act, CI.getFrontendOpts().ASTMergeFiles); + + return Act; +} + +//===----------------------------------------------------------------------===// +// Implementation of ClangExpressionParser +//===----------------------------------------------------------------------===// + +ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, + ClangExpression &expr) : + m_expr (expr), + m_compiler (), + m_code_generator () +{ + // Initialize targets first, so that --version shows registered targets. + static struct InitializeLLVM { + InitializeLLVM() { + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllDisassemblers(); + + llvm::DisablePrettyStackTrace = true; + } + } InitializeLLVM; + + // 1. Create a new compiler instance. + m_compiler.reset(new CompilerInstance()); + + // 2. Install the target. + + lldb::TargetSP target_sp; + if (exe_scope) + target_sp = exe_scope->CalculateTarget(); + + // TODO: figure out what to really do when we don't have a valid target. + // 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. This needs to be fixed in a better way. + if (target_sp && target_sp->GetArchitecture().IsValid()) + { + std::string triple = target_sp->GetArchitecture().GetTriple().str(); + + int dash_count = 0; + for (size_t i = 0; i < triple.size(); ++i) + { + if (triple[i] == '-') + dash_count++; + if (dash_count == 3) + { + triple.resize(i); + break; + } + } + + m_compiler->getTargetOpts().Triple = triple; + } + else + { + m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); + } + + if (target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86 || + target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86_64) + { + m_compiler->getTargetOpts().Features.push_back("+sse"); + m_compiler->getTargetOpts().Features.push_back("+sse2"); + } + + if (m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) + m_compiler->getTargetOpts().ABI = "apcs-gnu"; + + m_compiler->createDiagnostics(); + + // Create the target instance. + m_compiler->setTarget(TargetInfo::CreateTargetInfo(m_compiler->getDiagnostics(), + &m_compiler->getTargetOpts())); + + assert (m_compiler->hasTarget()); + + // 3. Set options. + + lldb::LanguageType language = expr.Language(); + + switch (language) + { + case lldb::eLanguageTypeC: + break; + case lldb::eLanguageTypeObjC: + m_compiler->getLangOpts().ObjC1 = true; + m_compiler->getLangOpts().ObjC2 = true; + break; + case lldb::eLanguageTypeC_plus_plus: + m_compiler->getLangOpts().CPlusPlus = true; + m_compiler->getLangOpts().CPlusPlus11 = true; + break; + case lldb::eLanguageTypeObjC_plus_plus: + default: + m_compiler->getLangOpts().ObjC1 = true; + m_compiler->getLangOpts().ObjC2 = true; + m_compiler->getLangOpts().CPlusPlus = true; + m_compiler->getLangOpts().CPlusPlus11 = true; + break; + } + + m_compiler->getLangOpts().Bool = true; + m_compiler->getLangOpts().WChar = true; + m_compiler->getLangOpts().Blocks = true; + m_compiler->getLangOpts().DebuggerSupport = true; // Features specifically for debugger clients + if (expr.DesiredResultType() == ClangExpression::eResultTypeId) + m_compiler->getLangOpts().DebuggerCastResultToId = true; + + // 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. + m_compiler->getLangOpts().SpellChecking = false; + + lldb::ProcessSP process_sp; + if (exe_scope) + process_sp = exe_scope->CalculateProcess(); + + if (process_sp && m_compiler->getLangOpts().ObjC1) + { + if (process_sp->GetObjCLanguageRuntime()) + { + if (process_sp->GetObjCLanguageRuntime()->GetRuntimeVersion() == eAppleObjC_V2) + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7)); + else + m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::FragileMacOSX, VersionTuple(10, 7)); + + if (process_sp->GetObjCLanguageRuntime()->HasNewLiteralsAndIndexing()) + m_compiler->getLangOpts().DebuggerObjCLiteral = true; + } + } + + m_compiler->getLangOpts().ThreadsafeStatics = false; + m_compiler->getLangOpts().AccessControl = false; // Debuggers get universal access + m_compiler->getLangOpts().DollarIdents = true; // $ indicates a persistent variable name + + // Set CodeGen options + m_compiler->getCodeGenOpts().EmitDeclMetadata = true; + m_compiler->getCodeGenOpts().InstrumentFunctions = false; + + // Disable some warnings. + m_compiler->getDiagnostics().setDiagnosticGroupMapping("unused-value", clang::diag::MAP_IGNORE, SourceLocation()); + m_compiler->getDiagnostics().setDiagnosticGroupMapping("odr", clang::diag::MAP_IGNORE, 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().setForcedLangOptions(m_compiler->getLangOpts()); + + // 4. Set up the diagnostic buffer for reporting errors + + m_compiler->getDiagnostics().setClient(new clang::TextDiagnosticBuffer); + + // 5. Set up the source management objects inside the compiler + + clang::FileSystemOptions file_system_options; + m_file_manager.reset(new clang::FileManager(file_system_options)); + + if (!m_compiler->hasSourceManager()) + m_compiler->createSourceManager(*m_file_manager.get()); + + m_compiler->createFileManager(); + m_compiler->createPreprocessor(); + + // 6. Most of this we get from the CompilerInstance, but we + // also want to give the context an ExternalASTSource. + m_selector_table.reset(new SelectorTable()); + m_builtin_context.reset(new Builtin::Context()); + + std::unique_ptr ast_context(new ASTContext(m_compiler->getLangOpts(), + m_compiler->getSourceManager(), + &m_compiler->getTarget(), + m_compiler->getPreprocessor().getIdentifierTable(), + *m_selector_table.get(), + *m_builtin_context.get(), + 0)); + + ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); + + if (decl_map) + { + llvm::OwningPtr ast_source(decl_map->CreateProxy()); + decl_map->InstallASTContext(ast_context.get()); + ast_context->setExternalSource(ast_source); + } + + m_compiler->setASTContext(ast_context.release()); + + std::string module_name("$__lldb_module"); + + m_llvm_context.reset(new LLVMContext()); + m_code_generator.reset(CreateLLVMCodeGen(m_compiler->getDiagnostics(), + module_name, + m_compiler->getCodeGenOpts(), + m_compiler->getTargetOpts(), + *m_llvm_context)); +} + +ClangExpressionParser::~ClangExpressionParser() +{ +} + +unsigned +ClangExpressionParser::Parse (Stream &stream) +{ + TextDiagnosticBuffer *diag_buf = static_cast(m_compiler->getDiagnostics().getClient()); + + diag_buf->FlushDiagnostics (m_compiler->getDiagnostics()); + + MemoryBuffer *memory_buffer = MemoryBuffer::getMemBufferCopy(m_expr.Text(), __FUNCTION__); + m_compiler->getSourceManager().createMainFileIDForMemBuffer (memory_buffer); + + diag_buf->BeginSourceFile(m_compiler->getLangOpts(), &m_compiler->getPreprocessor()); + + ASTConsumer *ast_transformer = m_expr.ASTTransformer(m_code_generator.get()); + + if (ast_transformer) + ParseAST(m_compiler->getPreprocessor(), ast_transformer, m_compiler->getASTContext()); + else + ParseAST(m_compiler->getPreprocessor(), m_code_generator.get(), m_compiler->getASTContext()); + + diag_buf->EndSourceFile(); + + TextDiagnosticBuffer::const_iterator diag_iterator; + + int num_errors = 0; + + for (diag_iterator = diag_buf->warn_begin(); + diag_iterator != diag_buf->warn_end(); + ++diag_iterator) + stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); + + num_errors = 0; + + for (diag_iterator = diag_buf->err_begin(); + diag_iterator != diag_buf->err_end(); + ++diag_iterator) + { + num_errors++; + stream.Printf("error: %s\n", (*diag_iterator).second.c_str()); + } + + for (diag_iterator = diag_buf->note_begin(); + diag_iterator != diag_buf->note_end(); + ++diag_iterator) + stream.Printf("note: %s\n", (*diag_iterator).second.c_str()); + + if (!num_errors) + { + if (m_expr.DeclMap() && !m_expr.DeclMap()->ResolveUnknownTypes()) + { + stream.Printf("error: Couldn't infer the type of a variable\n"); + num_errors++; + } + } + + return num_errors; +} + +static bool FindFunctionInModule (ConstString &mangled_name, + llvm::Module *module, + const char *orig_name) +{ + for (llvm::Module::iterator fi = module->getFunctionList().begin(), fe = module->getFunctionList().end(); + fi != fe; + ++fi) + { + if (fi->getName().str().find(orig_name) != std::string::npos) + { + mangled_name.SetCString(fi->getName().str().c_str()); + return true; + } + } + + return false; +} + +Error +ClangExpressionParser::PrepareForExecution (lldb::addr_t &func_addr, + lldb::addr_t &func_end, + std::unique_ptr &execution_unit_ap, + 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)); + + std::unique_ptr execution_engine_ap; + + Error err; + + std::unique_ptr module_ap (m_code_generator->ReleaseModule()); + + if (!module_ap.get()) + { + err.SetErrorToGenericError(); + err.SetErrorString("IR doesn't contain a module"); + return err; + } + + // Find the actual name of the function (it's often mangled somehow) + + ConstString function_name; + + if (!FindFunctionInModule(function_name, module_ap.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()); + } + + m_execution_unit.reset(new IRExecutionUnit(m_llvm_context, // handed off here + module_ap, // handed off here + function_name, + exe_ctx.GetTargetSP(), + m_compiler->getTargetOpts().Features)); + + ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); // result can be NULL + + if (decl_map) + { + Stream *error_stream = NULL; + Target *target = exe_ctx.GetTargetPtr(); + if (target) + error_stream = &target->GetDebugger().GetErrorStream(); + + IRForTarget ir_for_target(decl_map, + m_expr.NeedsVariableResolution(), + *m_execution_unit, + error_stream, + function_name.AsCString()); + + bool ir_can_run = ir_for_target.runOnModule(*m_execution_unit->GetModule()); + + Error interpret_error; + + can_interpret = IRInterpreter::CanInterpret(*m_execution_unit->GetModule(), *m_execution_unit->GetFunction(), interpret_error); + + Process *process = exe_ctx.GetProcessPtr(); + + if (!ir_can_run) + { + err.SetErrorString("The expression could not be prepared to run in the target"); + return err; + } + + 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 (execution_policy == eExecutionPolicyAlways || !can_interpret) + { + if (m_expr.NeedsValidation() && process) + { + if (!process->GetDynamicCheckers()) + { + DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); + + StreamString install_errors; + + if (!dynamic_checkers->Install(install_errors, exe_ctx)) + { + if (install_errors.GetString().empty()) + err.SetErrorString ("couldn't install checkers, unknown error"); + else + err.SetErrorString (install_errors.GetString().c_str()); + + return err; + } + + process->SetDynamicCheckers(dynamic_checkers); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Finished installing dynamic checkers =="); + } + + IRDynamicChecks ir_dynamic_checks(*process->GetDynamicCheckers(), function_name.AsCString()); + + if (!ir_dynamic_checks.runOnModule(*m_execution_unit->GetModule())) + { + err.SetErrorToGenericError(); + err.SetErrorString("Couldn't add dynamic checks to the expression"); + return err; + } + } + + m_execution_unit->GetRunnableInfo(err, func_addr, func_end); + } + } + else + { + m_execution_unit->GetRunnableInfo(err, func_addr, func_end); + } + + execution_unit_ap.reset (m_execution_unit.release()); + + return err; +} diff --git a/source/Expression/ClangExpressionVariable.cpp b/source/Expression/ClangExpressionVariable.cpp new file mode 100644 index 00000000000..0d355ce341c --- /dev/null +++ b/source/Expression/ClangExpressionVariable.cpp @@ -0,0 +1,136 @@ +//===-- ClangExpressionVariable.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangExpressionVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "clang/AST/ASTContext.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" + +using namespace lldb_private; +using namespace clang; + +ClangExpressionVariable::ClangExpressionVariable(ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size) : + m_parser_vars(), + m_jit_vars (), + m_flags (EVNone), + m_frozen_sp (ValueObjectConstResult::Create (exe_scope, byte_order, addr_byte_size)) +{ +} + +ClangExpressionVariable::ClangExpressionVariable (const lldb::ValueObjectSP &valobj_sp) : + m_parser_vars(), + m_jit_vars (), + m_flags (EVNone), + m_frozen_sp (valobj_sp) +{ +} + +//---------------------------------------------------------------------- +/// Return the variable's size in bytes +//---------------------------------------------------------------------- +size_t +ClangExpressionVariable::GetByteSize () +{ + return m_frozen_sp->GetByteSize(); +} + +const ConstString & +ClangExpressionVariable::GetName () +{ + return m_frozen_sp->GetName(); +} + +lldb::ValueObjectSP +ClangExpressionVariable::GetValueObject() +{ + return m_frozen_sp; +} + +RegisterInfo * +ClangExpressionVariable::GetRegisterInfo() +{ + return m_frozen_sp->GetValue().GetRegisterInfo(); +} + +void +ClangExpressionVariable::SetRegisterInfo (const RegisterInfo *reg_info) +{ + return m_frozen_sp->GetValue().SetContext (Value::eContextTypeRegisterInfo, const_cast(reg_info)); +} + +ClangASTType +ClangExpressionVariable::GetClangType() +{ + return m_frozen_sp->GetClangType(); +} + +void +ClangExpressionVariable::SetClangType(const ClangASTType &clang_type) +{ + m_frozen_sp->GetValue().SetClangType(clang_type); +} + + +TypeFromUser +ClangExpressionVariable::GetTypeFromUser() +{ + TypeFromUser tfu (m_frozen_sp->GetClangType()); + return tfu; +} + +uint8_t * +ClangExpressionVariable::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(m_frozen_sp->GetDataExtractor().GetDataStart()); + } + return NULL; +} + +void +ClangExpressionVariable::SetName (const ConstString &name) +{ + m_frozen_sp->SetName (name); +} + +void +ClangExpressionVariable::ValueUpdated () +{ + m_frozen_sp->ValueUpdated (); +} + +void +ClangExpressionVariable::TransferAddress (bool force) +{ + if (m_live_sp.get() == NULL) + return; + + if (m_frozen_sp.get() == NULL) + return; + + if (force || (m_frozen_sp->GetLiveAddress() == LLDB_INVALID_ADDRESS)) + m_frozen_sp->SetLiveAddress(m_live_sp->GetLiveAddress()); +} diff --git a/source/Expression/ClangFunction.cpp b/source/Expression/ClangFunction.cpp new file mode 100644 index 00000000000..177138df801 --- /dev/null +++ b/source/Expression/ClangFunction.cpp @@ -0,0 +1,633 @@ +//===-- ClangFunction.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +// C Includes +// C++ Includes +// Other libraries and framework includes +#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" + +// Project includes +#include "lldb/Expression/ASTStructExtractor.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/State.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectList.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.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/Core/Log.h" + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// ClangFunction constructor +//---------------------------------------------------------------------- +ClangFunction::ClangFunction +( + ExecutionContextScope &exe_scope, + const ClangASTType &return_type, + const Address& functionAddress, + const ValueList &arg_value_list +) : + m_function_ptr (NULL), + 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_arg_values (arg_value_list), + m_compiled (false), + m_JITted (false) +{ + m_jit_process_wp = lldb::ProcessWP(exe_scope.CalculateProcess()); + // Can't make a ClangFunction without a process. + assert (m_jit_process_wp.lock()); +} + +ClangFunction::ClangFunction +( + ExecutionContextScope &exe_scope, + Function &function, + ClangASTContext *ast_context, + const ValueList &arg_value_list +) : + m_function_ptr (&function), + m_function_addr (), + m_function_return_type (), + m_clang_ast_context (ast_context), + m_wrapper_function_name ("__lldb_function_caller"), + m_wrapper_struct_name ("__lldb_caller_struct"), + m_wrapper_args_addrs (), + 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 ClangFunction without a process. + assert (m_jit_process_wp.lock()); + + m_function_addr = m_function_ptr->GetAddressRange().GetBaseAddress(); + m_function_return_type = m_function_ptr->GetClangType().GetFunctionReturnType(); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ClangFunction::~ClangFunction() +{ +} + +unsigned +ClangFunction::CompileFunction (Stream &errors) +{ + if (m_compiled) + return 0; + + // 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()); + + // 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. + ClangASTType function_clang_type; + if (m_function_ptr) + { + function_clang_type = m_function_ptr->GetClangType(); + 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(); + } + else + { + ClangASTType clang_qual_type = m_arg_values.GetValueAtIndex(i)->GetClangType (); + if (clang_qual_type) + { + type_name = clang_qual_type.GetTypeName(); + } + else + { + errors.Printf("Could not determine type of input value %lu.", 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) + { + m_parser.reset(new ClangExpressionParser(jit_process_sp.get(), *this)); + + num_errors = m_parser->Parse (errors); + } + else + { + errors.Printf("no process - unable to inject function"); + num_errors = 1; + } + + m_compiled = (num_errors == 0); + + if (!m_compiled) + return num_errors; + + return num_errors; +} + +bool +ClangFunction::WriteFunctionWrapper (ExecutionContext &exe_ctx, Stream &errors) +{ + 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 + + Error jit_error (m_parser->PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + can_interpret, + eExecutionPolicyAlways)); + + if (!jit_error.Success()) + return false; + + if (process && m_jit_start_addr) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + + m_JITted = true; + + return true; +} + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, Stream &errors) +{ + return WriteFunctionArguments(exe_ctx, args_addr_ref, m_function_addr, m_arg_values, errors); +} + +// FIXME: Assure that the ValueList we were passed in is consistent with the one that defined this function. + +bool +ClangFunction::WriteFunctionArguments (ExecutionContext &exe_ctx, + lldb::addr_t &args_addr_ref, + Address function_address, + ValueList &arg_values, + Stream &errors) +{ + // All the information to reconstruct the struct is provided by the + // StructExtractor. + if (!m_struct_valid) + { + errors.Printf("Argument information was not correctly parsed, so the function cannot be called."); + return false; + } + + Error error; + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL) + 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 (function_address.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. + + Error value_error; + + size_t num_args = arg_values.GetSize(); + if (num_args != m_arg_values.GetSize()) + { + errors.Printf ("Wrong number of arguments - was: %lu should be: %lu", num_args, 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->GetClangType().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 +ClangFunction::InsertFunction (ExecutionContext &exe_ctx, lldb::addr_t &args_addr_ref, Stream &errors) +{ + using namespace clang; + + if (CompileFunction(errors) != 0) + return false; + if (!WriteFunctionWrapper(exe_ctx, errors)) + return false; + if (!WriteFunctionArguments(exe_ctx, args_addr_ref, errors)) + 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; +} + +ThreadPlan * +ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx, + lldb::addr_t func_addr, + lldb::addr_t &args_addr, + Stream &errors, + bool stop_others, + bool unwind_on_error, + bool ignore_breakpoints, + lldb::addr_t *this_arg, + lldb::addr_t *cmd_arg) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("-- [ClangFunction::GetThreadPlanToCallFunction] Creating thread plan to call function --"); + + // FIXME: Use the errors Stream for better error reporting. + Thread *thread = exe_ctx.GetThreadPtr(); + if (thread == NULL) + { + errors.Printf("Can't call a function without a valid thread."); + return NULL; + } + + // Okay, now run the function: + + Address wrapper_address (func_addr); + ThreadPlan *new_plan = new ThreadPlanCallFunction (*thread, + wrapper_address, + ClangASTType(), + args_addr, + stop_others, + unwind_on_error, + ignore_breakpoints, + this_arg, + cmd_arg); + new_plan->SetIsMasterPlan(true); + new_plan->SetOkayToDiscard (false); + return new_plan; +} + +bool +ClangFunction::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 ClangASTType, 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("-- [ClangFunction::FetchFunctionResults] Fetching function results --"); + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL) + return false; + + lldb::ProcessSP jit_process_sp(m_jit_process_wp.lock()); + + if (process != jit_process_sp.get()) + return false; + + Error error; + ret_value.GetScalar() = process->ReadUnsignedIntegerFromMemory (args_addr + m_return_offset, m_return_size, 0, error); + + if (error.Fail()) + return false; + + ret_value.SetClangType(m_function_return_type); + ret_value.SetValueType(Value::eValueTypeScalar); + return true; +} + +void +ClangFunction::DeallocateFunctionResults (ExecutionContext &exe_ctx, lldb::addr_t args_addr) +{ + std::list::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); +} + +ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, Value &results) +{ + return ExecuteFunction (exe_ctx, errors, 1000, true, results); +} + +ExecutionResults +ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, bool stop_others, Value &results) +{ + const bool try_all_threads = false; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, 0UL, try_all_threads, + unwind_on_error, ignore_breakpoints, results); +} + +ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exe_ctx, + Stream &errors, + uint32_t timeout_usec, + bool try_all_threads, + Value &results) +{ + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = true; + return ExecuteFunction (exe_ctx, NULL, errors, stop_others, timeout_usec, + try_all_threads, unwind_on_error, ignore_breakpoints, results); +} + +// This is the static function +ExecutionResults +ClangFunction::ExecuteFunction ( + ExecutionContext &exe_ctx, + lldb::addr_t function_address, + lldb::addr_t &void_arg, + bool stop_others, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + uint32_t timeout_usec, + Stream &errors, + lldb::addr_t *this_arg) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + if (log) + log->Printf("== [ClangFunction::ExecuteFunction] Executing function =="); + + lldb::ThreadPlanSP call_plan_sp (ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + function_address, + void_arg, + errors, + stop_others, + unwind_on_error, + ignore_breakpoints, + this_arg)); + if (!call_plan_sp) + return eExecutionSetupError; + + // 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); + + ExecutionResults results = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, call_plan_sp, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + errors); + + if (log) + { + if (results != eExecutionCompleted) + { + log->Printf("== [ClangFunction::ExecuteFunction] Execution completed abnormally =="); + } + else + { + log->Printf("== [ClangFunction::ExecuteFunction] Execution completed normally =="); + } + } + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + return results; +} + +ExecutionResults +ClangFunction::ExecuteFunction( + ExecutionContext &exe_ctx, + lldb::addr_t *args_addr_ptr, + Stream &errors, + bool stop_others, + uint32_t timeout_usec, + bool try_all_threads, + bool unwind_on_error, + bool ignore_breakpoints, + Value &results) +{ + using namespace clang; + ExecutionResults return_value = eExecutionSetupError; + + lldb::addr_t args_addr; + + if (args_addr_ptr != NULL) + args_addr = *args_addr_ptr; + else + args_addr = LLDB_INVALID_ADDRESS; + + if (CompileFunction(errors) != 0) + return eExecutionSetupError; + + if (args_addr == LLDB_INVALID_ADDRESS) + { + if (!InsertFunction(exe_ctx, args_addr, errors)) + return eExecutionSetupError; + } + + return_value = ClangFunction::ExecuteFunction (exe_ctx, + m_jit_start_addr, + args_addr, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + errors); + + if (args_addr_ptr != NULL) + *args_addr_ptr = args_addr; + + if (return_value != eExecutionCompleted) + return return_value; + + FetchFunctionResults(exe_ctx, args_addr, results); + + if (args_addr_ptr == NULL) + DeallocateFunctionResults(exe_ctx, args_addr); + + return eExecutionCompleted; +} + +clang::ASTConsumer * +ClangFunction::ASTTransformer (clang::ASTConsumer *passthrough) +{ + return new ASTStructExtractor(passthrough, m_wrapper_struct_name.c_str(), *this); +} diff --git a/source/Expression/ClangPersistentVariables.cpp b/source/Expression/ClangPersistentVariables.cpp new file mode 100644 index 00000000000..db062d2e20b --- /dev/null +++ b/source/Expression/ClangPersistentVariables.cpp @@ -0,0 +1,89 @@ +//===-- ClangPersistentVariables.cpp ----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ClangPersistentVariables.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Value.h" + +#include "llvm/ADT/StringMap.h" + +using namespace lldb; +using namespace lldb_private; + +ClangPersistentVariables::ClangPersistentVariables () : + ClangExpressionVariableList(), + m_next_persistent_variable_id (0) +{ +} + +ClangExpressionVariableSP +ClangPersistentVariables::CreatePersistentVariable (const lldb::ValueObjectSP &valobj_sp) +{ + ClangExpressionVariableSP var_sp (CreateVariable(valobj_sp)); + return var_sp; +} + +ClangExpressionVariableSP +ClangPersistentVariables::CreatePersistentVariable (ExecutionContextScope *exe_scope, + const ConstString &name, + const TypeFromUser& user_type, + lldb::ByteOrder byte_order, + uint32_t addr_byte_size) +{ + ClangExpressionVariableSP var_sp (GetVariable(name)); + + if (!var_sp) + var_sp = CreateVariable(exe_scope, name, user_type, byte_order, addr_byte_size); + + return var_sp; +} + +void +ClangPersistentVariables::RemovePersistentVariable (lldb::ClangExpressionVariableSP variable) +{ + RemoveVariable(variable); + + const char *name = variable->GetName().AsCString(); + + if (*name != '$') + return; + name++; + + if (strtoul(name, NULL, 0) == m_next_persistent_variable_id - 1) + m_next_persistent_variable_id--; +} + +ConstString +ClangPersistentVariables::GetNextPersistentVariableName () +{ + char name_cstr[256]; + ::snprintf (name_cstr, sizeof(name_cstr), "$%u", m_next_persistent_variable_id++); + ConstString name(name_cstr); + return name; +} + +void +ClangPersistentVariables::RegisterPersistentType (const ConstString &name, + clang::TypeDecl *type_decl) +{ + m_persistent_types.insert(std::pair(name.GetCString(), type_decl)); +} + +clang::TypeDecl * +ClangPersistentVariables::GetPersistentType (const ConstString &name) +{ + PersistentTypeMap::const_iterator i = m_persistent_types.find(name.GetCString()); + + if (i == m_persistent_types.end()) + return NULL; + else + return i->second; +} diff --git a/source/Expression/ClangUserExpression.cpp b/source/Expression/ClangUserExpression.cpp new file mode 100644 index 00000000000..7f09f6fa094 --- /dev/null +++ b/source/Expression/ClangUserExpression.cpp @@ -0,0 +1,1091 @@ +//===-- ClangUserExpression.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#if HAVE_SYS_TYPES_H +# include +#endif + +// C++ Includes +#include +#include +#include + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Expression/ASTResultSynthesizer.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangFunction.h" +#include "lldb/Expression/ClangUserExpression.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Expression/Materializer.h" +#include "lldb/Host/Host.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Function.h" +#include "lldb/Symbol/Type.h" +#include "lldb/Symbol/ClangExternalASTSourceCommon.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 "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" + +using namespace lldb_private; + +ClangUserExpression::ClangUserExpression (const char *expr, + const char *expr_prefix, + lldb::LanguageType language, + ResultType desired_type) : + ClangExpression (), + m_stack_frame_bottom (LLDB_INVALID_ADDRESS), + m_stack_frame_top (LLDB_INVALID_ADDRESS), + m_expr_text (expr), + m_expr_prefix (expr_prefix ? expr_prefix : ""), + m_language (language), + m_transformed_text (), + m_desired_type (desired_type), + m_enforce_valid_object (true), + m_cplusplus (false), + m_objectivec (false), + m_static_method(false), + m_needs_object_ptr (false), + m_const_object (false), + m_target (NULL), + m_can_interpret (false), + m_materialized_address (LLDB_INVALID_ADDRESS) +{ + 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 () +{ +} + +clang::ASTConsumer * +ClangUserExpression::ASTTransformer (clang::ASTConsumer *passthrough) +{ + ClangASTContext *clang_ast_context = m_target->GetScratchClangASTContext(); + + if (!clang_ast_context) + return NULL; + + if (!m_result_synthesizer.get()) + m_result_synthesizer.reset(new ASTResultSynthesizer(passthrough, + *m_target)); + + return m_result_synthesizer.get(); +} + +void +ClangUserExpression::ScanContext(ExecutionContext &exe_ctx, Error &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 == NULL) + { + 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; + } + + clang::DeclContext *decl_context = function_block->GetClangDeclContext(); + + if (!decl_context) + { + if (log) + log->Printf(" [CUE::SC] Null decl context"); + return; + } + + if (clang::CXXMethodDecl *method_decl = llvm::dyn_cast(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_cplusplus = true; + m_needs_object_ptr = true; + } + } + else if (clang::ObjCMethodDecl *method_decl = llvm::dyn_cast(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_objectivec = true; + m_needs_object_ptr = true; + + if (!method_decl->isInstanceMethod()) + m_static_method = true; + } + } + else if (clang::FunctionDecl *function_decl = llvm::dyn_cast(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 it 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::GetMetadata (&decl_context->getParentASTContext(), 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_cplusplus = 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; + } + + ClangASTType self_clang_type = self_type->GetClangForwardType(); + + if (!self_clang_type) + { + err.SetErrorString(selfErrorString); + return; + } + + if (self_clang_type.IsObjCClassType()) + { + return; + } + else if (self_clang_type.IsObjCObjectPointerType()) + { + m_objectivec = true; + m_needs_object_ptr = true; + } + else + { + err.SetErrorString(selfErrorString); + return; + } + } + else + { + m_objectivec = true; + m_needs_object_ptr = true; + } + } + } + } +} + +void +ClangUserExpression::InstallContext (ExecutionContext &exe_ctx) +{ + m_process_wp = exe_ctx.GetProcessSP(); + + lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP(); + + if (frame_sp) + m_address = frame_sp->GetFrameCodeAddress(); +} + +bool +ClangUserExpression::LockAndCheckContext (ExecutionContext &exe_ctx, + lldb::TargetSP &target_sp, + lldb::ProcessSP &process_sp, + lldb::StackFrameSP &frame_sp) +{ + lldb::ProcessSP expected_process_sp = m_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 +ClangUserExpression::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); +} + +// 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 +} + +// Another hack, meant to allow use of unichar despite it not being available in +// the type information. Although we could special-case it in type lookup, +// hopefully we'll figure out a way to #include the same environment as is +// present in the original source file rather than try to hack specific type +// definitions in as needed. +static void +ApplyUnicharHack(std::string &expr) +{ +#define UNICHAR_HACK_FROM "unichar" +#define UNICHAR_HACK_TO "unsigned short" + + size_t from_offset; + + while ((from_offset = expr.find(UNICHAR_HACK_FROM)) != expr.npos) + expr.replace(from_offset, sizeof(UNICHAR_HACK_FROM) - 1, UNICHAR_HACK_TO); + +#undef UNICHAR_HACK_TO +#undef UNICHAR_HACK_FROM +} + +bool +ClangUserExpression::Parse (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + bool keep_result_in_memory) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Error err; + + InstallContext(exe_ctx); + + ScanContext(exe_ctx, err); + + if (!err.Success()) + { + error_stream.Printf("warning: %s\n", err.AsCString()); + } + + StreamString m_transformed_stream; + + //////////////////////////////////// + // Generate the expression + // + + ApplyObjcCastHack(m_expr_text); + //ApplyUnicharHack(m_expr_text); + + std::unique_ptr source_code (ExpressionSourceCode::CreateWrapped(m_expr_prefix.c_str(), m_expr_text.c_str())); + + lldb::LanguageType lang_type; + + if (m_cplusplus) + lang_type = lldb::eLanguageTypeC_plus_plus; + else if(m_objectivec) + lang_type = lldb::eLanguageTypeObjC; + else + lang_type = lldb::eLanguageTypeC; + + if (!source_code->GetText(m_transformed_text, lang_type, m_const_object, m_static_method)) + { + error_stream.PutCString ("error: couldn't construct expression body"); + 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) + { + error_stream.PutCString ("error: invalid target\n"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + m_materializer_ap.reset(new Materializer()); + + m_expr_decl_map.reset(new ClangExpressionDeclMap(keep_result_in_memory, exe_ctx)); + + class OnExit + { + public: + typedef std::function Callback; + + OnExit (Callback const &callback) : + m_callback(callback) + { + } + + ~OnExit () + { + m_callback(); + } + private: + Callback m_callback; + }; + + OnExit on_exit([this]() { m_expr_decl_map.reset(); }); + + if (!m_expr_decl_map->WillParse(exe_ctx, m_materializer_ap.get())) + { + error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + ExecutionContextScope *exe_scope = process; + + if (!exe_scope) + exe_scope = exe_ctx.GetTargetPtr(); + + ClangExpressionParser parser(exe_scope, *this); + + unsigned num_errors = parser.Parse (error_stream); + + if (num_errors) + { + error_stream.Printf ("error: %d errors parsing expression\n", num_errors); + + m_expr_decl_map->DidParse(); + + return false; + } + + ////////////////////////////////////////////////////////////////////////////////////////// + // Prepare the output of the parser for execution, evaluating it statically if possible + // + + Error jit_error = parser.PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + m_can_interpret, + execution_policy); + + if (jit_error.Success()) + { + if (process && m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + return true; + } + else + { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + error_stream.Printf ("error: %s\n", error_cstr); + else + error_stream.Printf ("error: expression can't be interpreted or run\n"); + return false; + } +} + +static lldb::addr_t +GetObjectPointer (lldb::StackFrameSP frame_sp, + ConstString &object_name, + Error &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::eExpressionPathOptionsAllowDirectIVarAccess || + StackFrame::eExpressionPathOptionsNoFragileObjcIvar || + StackFrame::eExpressionPathOptionsNoSyntheticChildren || + StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, + var_sp, + err); + + if (!err.Success()) + 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; +} + +bool +ClangUserExpression::PrepareToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::addr_t &struct_address, + lldb::addr_t &object_ptr, + lldb::addr_t &cmd_ptr) +{ + lldb::TargetSP target; + lldb::ProcessSP process; + lldb::StackFrameSP frame; + + if (!LockAndCheckContext(exe_ctx, + target, + process, + frame)) + { + error_stream.Printf("The context has changed before we could JIT the expression!\n"); + return false; + } + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) + { + if (m_needs_object_ptr) + { + ConstString object_name; + + if (m_cplusplus) + { + object_name.SetCString("this"); + } + else if (m_objectivec) + { + object_name.SetCString("self"); + } + else + { + error_stream.Printf("Need object pointer but don't know the language\n"); + return false; + } + + Error object_ptr_error; + + object_ptr = GetObjectPointer(frame, object_name, object_ptr_error); + + if (!object_ptr_error.Success()) + { + error_stream.Printf("warning: couldn't get required object pointer (substituting NULL): %s\n", object_ptr_error.AsCString()); + object_ptr = 0; + } + + if (m_objectivec) + { + ConstString cmd_name("_cmd"); + + cmd_ptr = GetObjectPointer(frame, cmd_name, object_ptr_error); + + if (!object_ptr_error.Success()) + { + error_stream.Printf("warning: couldn't get cmd pointer (substituting NULL): %s\n", object_ptr_error.AsCString()); + cmd_ptr = 0; + } + } + } + + if (m_materialized_address == LLDB_INVALID_ADDRESS) + { + Error alloc_error; + + IRMemoryMap::AllocationPolicy policy = m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly : IRMemoryMap::eAllocationPolicyMirror; + + m_materialized_address = m_execution_unit_ap->Malloc(m_materializer_ap->GetStructByteSize(), + m_materializer_ap->GetStructAlignment(), + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + policy, + alloc_error); + + if (!alloc_error.Success()) + { + error_stream.Printf("Couldn't allocate space for materialized struct: %s\n", alloc_error.AsCString()); + return false; + } + } + + struct_address = m_materialized_address; + + if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) + { + Error alloc_error; + + const size_t stack_frame_size = 512 * 1024; + + m_stack_frame_bottom = m_execution_unit_ap->Malloc(stack_frame_size, + 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyHostOnly, + alloc_error); + + m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; + + if (!alloc_error.Success()) + { + error_stream.Printf("Couldn't allocate space for the stack frame: %s\n", alloc_error.AsCString()); + return false; + } + } + + Error materialize_error; + + m_dematerializer_sp = m_materializer_ap->Materialize(frame, *m_execution_unit_ap, struct_address, materialize_error); + + if (!materialize_error.Success()) + { + error_stream.Printf("Couldn't materialize struct: %s\n", materialize_error.AsCString()); + return false; + } + } + return true; +} + +ThreadPlan * +ClangUserExpression::GetThreadPlanToExecuteJITExpression (Stream &error_stream, + ExecutionContext &exe_ctx) +{ + lldb::addr_t struct_address; + + lldb::addr_t object_ptr = 0; + lldb::addr_t cmd_ptr = 0; + + PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr, cmd_ptr); + + // FIXME: This should really return a ThreadPlanCallUserExpression, in order to make sure that we don't release the + // ClangUserExpression resources before the thread plan finishes execution in the target. But because we are + // forcing unwind_on_error to be true here, in practical terms that can't happen. + + const bool stop_others = true; + const bool unwind_on_error = true; + const bool ignore_breakpoints = false; + return ClangFunction::GetThreadPlanToCallFunction (exe_ctx, + m_jit_start_addr, + struct_address, + error_stream, + stop_others, + unwind_on_error, + ignore_breakpoints, + (m_needs_object_ptr ? &object_ptr : NULL), + (m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL); +} + +bool +ClangUserExpression::FinalizeJITExecution (Stream &error_stream, + ExecutionContext &exe_ctx, + lldb::ClangExpressionVariableSP &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("-- [ClangUserExpression::FinalizeJITExecution] Dematerializing after execution --"); + + if (!m_dematerializer_sp) + { + error_stream.Printf ("Couldn't apply expression side effects : no dematerializer is present"); + return false; + } + + Error dematerialize_error; + + m_dematerializer_sp->Dematerialize(dematerialize_error, result, function_stack_bottom, function_stack_top); + + if (!dematerialize_error.Success()) + { + error_stream.Printf ("Couldn't apply expression side effects : %s\n", dematerialize_error.AsCString("unknown error")); + return false; + } + + if (result) + result->TransferAddress(); + + m_dematerializer_sp.reset(); + + return true; +} + +ExecutionResults +ClangUserExpression::Execute (Stream &error_stream, + ExecutionContext &exe_ctx, + bool unwind_on_error, + bool ignore_breakpoints, + ClangUserExpression::ClangUserExpressionSP &shared_ptr_to_me, + lldb::ClangExpressionVariableSP &result, + bool run_others, + uint32_t timeout_usec) +{ + // 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; + + lldb::addr_t object_ptr = 0; + lldb::addr_t cmd_ptr = 0; + + if (!PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr, cmd_ptr)) + { + error_stream.Printf("Errored out in %s, couldn't PrepareToExecuteJITExpression", __FUNCTION__); + return eExecutionSetupError; + } + + 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_ap->GetModule(); + llvm::Function *function = m_execution_unit_ap->GetFunction(); + + if (!module || !function) + { + error_stream.Printf("Supposed to interpret, but nothing is there"); + return eExecutionSetupError; + } + + Error interpreter_error; + + llvm::SmallVector args; + + if (m_needs_object_ptr) + { + args.push_back(object_ptr); + + if (m_objectivec) + args.push_back(cmd_ptr); + } + + args.push_back(struct_address); + + function_stack_bottom = m_stack_frame_bottom; + function_stack_top = m_stack_frame_top; + + IRInterpreter::Interpret (*module, + *function, + args, + *m_execution_unit_ap.get(), + interpreter_error, + function_stack_bottom, + function_stack_top); + + if (!interpreter_error.Success()) + { + error_stream.Printf("Supposed to interpret, but failed: %s", interpreter_error.AsCString()); + return eExecutionDiscarded; + } + } + else + { + const bool stop_others = true; + const bool try_all_threads = run_others; + + Address wrapper_address (m_jit_start_addr); + lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (exe_ctx.GetThreadRef(), + wrapper_address, + struct_address, + stop_others, + unwind_on_error, + ignore_breakpoints, + (m_needs_object_ptr ? &object_ptr : NULL), + ((m_needs_object_ptr && m_objectivec) ? &cmd_ptr : NULL), + shared_ptr_to_me)); + + if (!call_plan_sp || !call_plan_sp->ValidatePlan (&error_stream)) + return eExecutionSetupError; + + lldb::addr_t function_stack_pointer = static_cast(call_plan_sp.get())->GetFunctionStackPointer(); + + function_stack_bottom = function_stack_pointer - Host::GetPageSize(); + function_stack_top = function_stack_pointer; + + if (log) + log->Printf("-- [ClangUserExpression::Execute] Execution of expression begins --"); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); + + ExecutionResults execution_result = exe_ctx.GetProcessRef().RunThreadPlan (exe_ctx, + call_plan_sp, + stop_others, + try_all_threads, + unwind_on_error, + ignore_breakpoints, + timeout_usec, + error_stream); + + if (exe_ctx.GetProcessPtr()) + exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); + + if (log) + log->Printf("-- [ClangUserExpression::Execute] Execution of expression completed --"); + + if (execution_result == eExecutionInterrupted || execution_result == eExecutionHitBreakpoint) + { + const char *error_desc = NULL; + + 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) + error_stream.Printf ("Execution was interrupted, reason: %s.", error_desc); + else + error_stream.Printf ("Execution was interrupted."); + + if ((execution_result == eExecutionInterrupted && unwind_on_error) + || (execution_result == eExecutionHitBreakpoint && ignore_breakpoints)) + error_stream.Printf ("\nThe process has been returned to the state before expression evaluation."); + else + error_stream.Printf ("\nThe 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 != eExecutionCompleted) + { + error_stream.Printf ("Couldn't execute function; result was %s\n", Process::ExecutionResultAsCString (execution_result)); + return execution_result; + } + } + + if (FinalizeJITExecution (error_stream, exe_ctx, result, function_stack_bottom, function_stack_top)) + { + return eExecutionCompleted; + } + else + { + return eExecutionSetupError; + } + } + else + { + error_stream.Printf("Expression can't be run, because there is no JIT compiled function"); + return eExecutionSetupError; + } +} + +ExecutionResults +ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + bool run_others, + uint32_t timeout_usec) +{ + Error error; + return EvaluateWithError (exe_ctx, + execution_policy, + language, + desired_type, + unwind_on_error, + ignore_breakpoints, + expr_cstr, + expr_prefix, + result_valobj_sp, + error, + run_others, + timeout_usec); +} + +ExecutionResults +ClangUserExpression::EvaluateWithError (ExecutionContext &exe_ctx, + lldb_private::ExecutionPolicy execution_policy, + lldb::LanguageType language, + ResultType desired_type, + bool unwind_on_error, + bool ignore_breakpoints, + const char *expr_cstr, + const char *expr_prefix, + lldb::ValueObjectSP &result_valobj_sp, + Error &error, + bool run_others, + uint32_t timeout_usec) +{ + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_EXPRESSIONS | LIBLLDB_LOG_STEP)); + + ExecutionResults execution_results = eExecutionSetupError; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process == NULL || process->GetState() != lldb::eStateStopped) + { + if (execution_policy == eExecutionPolicyAlways) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Expression may not run, but is not constant =="); + + error.SetErrorString ("expression needed to run but couldn't"); + + return execution_results; + } + } + + if (process == NULL || !process->CanJIT()) + execution_policy = eExecutionPolicyNever; + + ClangUserExpressionSP user_expression_sp (new ClangUserExpression (expr_cstr, expr_prefix, language, desired_type)); + + StreamString error_stream; + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Parsing expression %s ==", expr_cstr); + + const bool keep_expression_in_memory = true; + + if (!user_expression_sp->Parse (error_stream, exe_ctx, execution_policy, keep_expression_in_memory)) + { + if (error_stream.GetString().empty()) + error.SetErrorString ("expression failed to parse, unknown error"); + else + error.SetErrorString (error_stream.GetString().c_str()); + } + else + { + lldb::ClangExpressionVariableSP expr_result; + + if (execution_policy == eExecutionPolicyNever && + !user_expression_sp->CanInterpret()) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Expression may not run, but is not constant =="); + + if (error_stream.GetString().empty()) + error.SetErrorString ("expression needed to run but couldn't"); + } + else + { + error_stream.GetString().clear(); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Executing expression =="); + + execution_results = user_expression_sp->Execute (error_stream, + exe_ctx, + unwind_on_error, + ignore_breakpoints, + user_expression_sp, + expr_result, + run_others, + timeout_usec); + + if (execution_results != eExecutionCompleted) + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed abnormally =="); + + if (error_stream.GetString().empty()) + error.SetErrorString ("expression failed to execute, unknown error"); + else + error.SetErrorString (error_stream.GetString().c_str()); + } + else + { + if (expr_result) + { + result_valobj_sp = expr_result->GetValueObject(); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed normally with result %s ==", result_valobj_sp->GetValueAsCString()); + } + else + { + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Execution completed normally with no result =="); + + error.SetError(ClangUserExpression::kNoResult, lldb::eErrorTypeGeneric); + } + } + } + } + + if (result_valobj_sp.get() == NULL) + result_valobj_sp = ValueObjectConstResult::Create (NULL, error); + + return execution_results; +} diff --git a/source/Expression/ClangUtilityFunction.cpp b/source/Expression/ClangUtilityFunction.cpp new file mode 100644 index 00000000000..c911c279993 --- /dev/null +++ b/source/Expression/ClangUtilityFunction.cpp @@ -0,0 +1,169 @@ +//===-- ClangUserExpression.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#if HAVE_SYS_TYPES_H +# include +#endif + +// C++ Includes + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionParser.h" +#include "lldb/Expression/ClangUtilityFunction.h" +#include "lldb/Expression/ExpressionSourceCode.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Host/Host.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.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 (const char *text, + const char *name) : + ClangExpression (), + m_function_text (ExpressionSourceCode::g_expression_prefix), + m_function_name (name) +{ + if (text && text[0]) + m_function_text.append (text); +} + +ClangUtilityFunction::~ClangUtilityFunction () +{ +} + +//------------------------------------------------------------------ +/// Install the utility function into a process +/// +/// @param[in] error_stream +/// A stream to print parse 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 (Stream &error_stream, + ExecutionContext &exe_ctx) +{ + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) + { + error_stream.PutCString("error: already installed\n"); + return false; + } + + //////////////////////////////////// + // Set up the target and compiler + // + + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + { + error_stream.PutCString ("error: invalid target\n"); + return false; + } + + Process *process = exe_ctx.GetProcessPtr(); + + if (!process) + { + error_stream.PutCString ("error: invalid process\n"); + return false; + } + + ////////////////////////// + // Parse the expression + // + + bool keep_result_in_memory = false; + + m_expr_decl_map.reset(new ClangExpressionDeclMap(keep_result_in_memory, exe_ctx)); + + if (!m_expr_decl_map->WillParse(exe_ctx, NULL)) + { + error_stream.PutCString ("error: current process state is unsuitable for expression parsing\n"); + return false; + } + + ClangExpressionParser parser(exe_ctx.GetBestExecutionContextScope(), *this); + + unsigned num_errors = parser.Parse (error_stream); + + if (num_errors) + { + error_stream.Printf ("error: %d errors parsing expression\n", num_errors); + + m_expr_decl_map.reset(); + + return false; + } + + ////////////////////////////////// + // JIT the output of the parser + // + + bool can_interpret = false; // should stay that way + + Error jit_error = parser.PrepareForExecution (m_jit_start_addr, + m_jit_end_addr, + m_execution_unit_ap, + exe_ctx, + can_interpret, + eExecutionPolicyAlways); + + if (m_jit_start_addr != LLDB_INVALID_ADDRESS) + m_jit_process_wp = lldb::ProcessWP(process->shared_from_this()); + +#if 0 + // jingham: look here + StreamFile logfile ("/tmp/exprs.txt", "a"); + logfile.Printf ("0x%16.16" PRIx64 ": func = %s, source =\n%s\n", + m_jit_start_addr, + m_function_name.c_str(), + m_function_text.c_str()); +#endif + + m_expr_decl_map->DidParse(); + + m_expr_decl_map.reset(); + + if (jit_error.Success()) + { + return true; + } + else + { + const char *error_cstr = jit_error.AsCString(); + if (error_cstr && error_cstr[0]) + error_stream.Printf ("error: %s\n", error_cstr); + else + error_stream.Printf ("error: expression can't be interpreted or run\n"); + return false; + } +} + + diff --git a/source/Expression/DWARFExpression.cpp b/source/Expression/DWARFExpression.cpp new file mode 100644 index 00000000000..e2ae19e5ac7 --- /dev/null +++ b/source/Expression/DWARFExpression.cpp @@ -0,0 +1,2691 @@ +//===-- DWARFExpression.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/DWARFExpression.h" + +#include + +#include "lldb/Core/DataEncoder.h" +#include "lldb/Core/dwarf.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/VMRange.h" + +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/ClangExpressionVariable.h" + +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" + +#include "lldb/lldb-private-log.h" + +#include "lldb/Symbol/ClangASTType.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.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" + +using namespace lldb; +using namespace lldb_private; + +const char * +DW_OP_value_to_name (uint32_t val) +{ + static char invalid[100]; + switch (val) { + case 0x03: return "DW_OP_addr"; + case 0x06: return "DW_OP_deref"; + case 0x08: return "DW_OP_const1u"; + case 0x09: return "DW_OP_const1s"; + case 0x0a: return "DW_OP_const2u"; + case 0x0b: return "DW_OP_const2s"; + case 0x0c: return "DW_OP_const4u"; + case 0x0d: return "DW_OP_const4s"; + case 0x0e: return "DW_OP_const8u"; + case 0x0f: return "DW_OP_const8s"; + case 0x10: return "DW_OP_constu"; + case 0x11: return "DW_OP_consts"; + case 0x12: return "DW_OP_dup"; + case 0x13: return "DW_OP_drop"; + case 0x14: return "DW_OP_over"; + case 0x15: return "DW_OP_pick"; + case 0x16: return "DW_OP_swap"; + case 0x17: return "DW_OP_rot"; + case 0x18: return "DW_OP_xderef"; + case 0x19: return "DW_OP_abs"; + case 0x1a: return "DW_OP_and"; + case 0x1b: return "DW_OP_div"; + case 0x1c: return "DW_OP_minus"; + case 0x1d: return "DW_OP_mod"; + case 0x1e: return "DW_OP_mul"; + case 0x1f: return "DW_OP_neg"; + case 0x20: return "DW_OP_not"; + case 0x21: return "DW_OP_or"; + case 0x22: return "DW_OP_plus"; + case 0x23: return "DW_OP_plus_uconst"; + case 0x24: return "DW_OP_shl"; + case 0x25: return "DW_OP_shr"; + case 0x26: return "DW_OP_shra"; + case 0x27: return "DW_OP_xor"; + case 0x2f: return "DW_OP_skip"; + case 0x28: return "DW_OP_bra"; + case 0x29: return "DW_OP_eq"; + case 0x2a: return "DW_OP_ge"; + case 0x2b: return "DW_OP_gt"; + case 0x2c: return "DW_OP_le"; + case 0x2d: return "DW_OP_lt"; + case 0x2e: return "DW_OP_ne"; + case 0x30: return "DW_OP_lit0"; + case 0x31: return "DW_OP_lit1"; + case 0x32: return "DW_OP_lit2"; + case 0x33: return "DW_OP_lit3"; + case 0x34: return "DW_OP_lit4"; + case 0x35: return "DW_OP_lit5"; + case 0x36: return "DW_OP_lit6"; + case 0x37: return "DW_OP_lit7"; + case 0x38: return "DW_OP_lit8"; + case 0x39: return "DW_OP_lit9"; + case 0x3a: return "DW_OP_lit10"; + case 0x3b: return "DW_OP_lit11"; + case 0x3c: return "DW_OP_lit12"; + case 0x3d: return "DW_OP_lit13"; + case 0x3e: return "DW_OP_lit14"; + case 0x3f: return "DW_OP_lit15"; + case 0x40: return "DW_OP_lit16"; + case 0x41: return "DW_OP_lit17"; + case 0x42: return "DW_OP_lit18"; + case 0x43: return "DW_OP_lit19"; + case 0x44: return "DW_OP_lit20"; + case 0x45: return "DW_OP_lit21"; + case 0x46: return "DW_OP_lit22"; + case 0x47: return "DW_OP_lit23"; + case 0x48: return "DW_OP_lit24"; + case 0x49: return "DW_OP_lit25"; + case 0x4a: return "DW_OP_lit26"; + case 0x4b: return "DW_OP_lit27"; + case 0x4c: return "DW_OP_lit28"; + case 0x4d: return "DW_OP_lit29"; + case 0x4e: return "DW_OP_lit30"; + case 0x4f: return "DW_OP_lit31"; + case 0x50: return "DW_OP_reg0"; + case 0x51: return "DW_OP_reg1"; + case 0x52: return "DW_OP_reg2"; + case 0x53: return "DW_OP_reg3"; + case 0x54: return "DW_OP_reg4"; + case 0x55: return "DW_OP_reg5"; + case 0x56: return "DW_OP_reg6"; + case 0x57: return "DW_OP_reg7"; + case 0x58: return "DW_OP_reg8"; + case 0x59: return "DW_OP_reg9"; + case 0x5a: return "DW_OP_reg10"; + case 0x5b: return "DW_OP_reg11"; + case 0x5c: return "DW_OP_reg12"; + case 0x5d: return "DW_OP_reg13"; + case 0x5e: return "DW_OP_reg14"; + case 0x5f: return "DW_OP_reg15"; + case 0x60: return "DW_OP_reg16"; + case 0x61: return "DW_OP_reg17"; + case 0x62: return "DW_OP_reg18"; + case 0x63: return "DW_OP_reg19"; + case 0x64: return "DW_OP_reg20"; + case 0x65: return "DW_OP_reg21"; + case 0x66: return "DW_OP_reg22"; + case 0x67: return "DW_OP_reg23"; + case 0x68: return "DW_OP_reg24"; + case 0x69: return "DW_OP_reg25"; + case 0x6a: return "DW_OP_reg26"; + case 0x6b: return "DW_OP_reg27"; + case 0x6c: return "DW_OP_reg28"; + case 0x6d: return "DW_OP_reg29"; + case 0x6e: return "DW_OP_reg30"; + case 0x6f: return "DW_OP_reg31"; + case 0x70: return "DW_OP_breg0"; + case 0x71: return "DW_OP_breg1"; + case 0x72: return "DW_OP_breg2"; + case 0x73: return "DW_OP_breg3"; + case 0x74: return "DW_OP_breg4"; + case 0x75: return "DW_OP_breg5"; + case 0x76: return "DW_OP_breg6"; + case 0x77: return "DW_OP_breg7"; + case 0x78: return "DW_OP_breg8"; + case 0x79: return "DW_OP_breg9"; + case 0x7a: return "DW_OP_breg10"; + case 0x7b: return "DW_OP_breg11"; + case 0x7c: return "DW_OP_breg12"; + case 0x7d: return "DW_OP_breg13"; + case 0x7e: return "DW_OP_breg14"; + case 0x7f: return "DW_OP_breg15"; + case 0x80: return "DW_OP_breg16"; + case 0x81: return "DW_OP_breg17"; + case 0x82: return "DW_OP_breg18"; + case 0x83: return "DW_OP_breg19"; + case 0x84: return "DW_OP_breg20"; + case 0x85: return "DW_OP_breg21"; + case 0x86: return "DW_OP_breg22"; + case 0x87: return "DW_OP_breg23"; + case 0x88: return "DW_OP_breg24"; + case 0x89: return "DW_OP_breg25"; + case 0x8a: return "DW_OP_breg26"; + case 0x8b: return "DW_OP_breg27"; + case 0x8c: return "DW_OP_breg28"; + case 0x8d: return "DW_OP_breg29"; + case 0x8e: return "DW_OP_breg30"; + case 0x8f: return "DW_OP_breg31"; + case 0x90: return "DW_OP_regx"; + case 0x91: return "DW_OP_fbreg"; + case 0x92: return "DW_OP_bregx"; + case 0x93: return "DW_OP_piece"; + case 0x94: return "DW_OP_deref_size"; + case 0x95: return "DW_OP_xderef_size"; + case 0x96: return "DW_OP_nop"; + case 0x97: return "DW_OP_push_object_address"; + case 0x98: return "DW_OP_call2"; + case 0x99: return "DW_OP_call4"; + case 0x9a: return "DW_OP_call_ref"; +// case DW_OP_APPLE_array_ref: return "DW_OP_APPLE_array_ref"; +// case DW_OP_APPLE_extern: return "DW_OP_APPLE_extern"; + case DW_OP_APPLE_uninit: return "DW_OP_APPLE_uninit"; +// case DW_OP_APPLE_assign: return "DW_OP_APPLE_assign"; +// case DW_OP_APPLE_address_of: return "DW_OP_APPLE_address_of"; +// case DW_OP_APPLE_value_of: return "DW_OP_APPLE_value_of"; +// case DW_OP_APPLE_deref_type: return "DW_OP_APPLE_deref_type"; +// case DW_OP_APPLE_expr_local: return "DW_OP_APPLE_expr_local"; +// case DW_OP_APPLE_constf: return "DW_OP_APPLE_constf"; +// case DW_OP_APPLE_scalar_cast: return "DW_OP_APPLE_scalar_cast"; +// case DW_OP_APPLE_clang_cast: return "DW_OP_APPLE_clang_cast"; +// case DW_OP_APPLE_clear: return "DW_OP_APPLE_clear"; +// case DW_OP_APPLE_error: return "DW_OP_APPLE_error"; + default: + snprintf (invalid, sizeof(invalid), "Unknown DW_OP constant: 0x%x", val); + return invalid; + } +} + + +//---------------------------------------------------------------------- +// DWARFExpression constructor +//---------------------------------------------------------------------- +DWARFExpression::DWARFExpression() : + m_data(), + m_reg_kind (eRegisterKindDWARF), + m_loclist_slide (LLDB_INVALID_ADDRESS) +{ +} + +DWARFExpression::DWARFExpression(const DWARFExpression& rhs) : + m_data(rhs.m_data), + m_reg_kind (rhs.m_reg_kind), + m_loclist_slide(rhs.m_loclist_slide) +{ +} + + +DWARFExpression::DWARFExpression(const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) : + m_data(data, data_offset, data_length), + m_reg_kind (eRegisterKindDWARF), + m_loclist_slide(LLDB_INVALID_ADDRESS) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +DWARFExpression::~DWARFExpression() +{ +} + + +bool +DWARFExpression::IsValid() const +{ + return m_data.GetByteSize() > 0; +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data) +{ + m_data = data; +} + +void +DWARFExpression::CopyOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +{ + const uint8_t *bytes = data.PeekData(data_offset, data_length); + if (bytes) + { + m_data.SetData(DataBufferSP(new DataBufferHeap(bytes, data_length))); + m_data.SetByteOrder(data.GetByteOrder()); + m_data.SetAddressByteSize(data.GetAddressByteSize()); + } +} + +void +DWARFExpression::SetOpcodeData (const DataExtractor& data, lldb::offset_t data_offset, lldb::offset_t data_length) +{ + m_data.SetData(data, data_offset, data_length); +} + +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 (offset > start_offset) + s->PutChar(' '); + break; + + case lldb::eDescriptionLevelFull: + case lldb::eDescriptionLevelVerbose: + if (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 << "form_tls_address"; break; // 0x9b DWARF3 +// case DW_OP_call_frame_cfa: s << "call_frame_cfa"; break; // 0x9c DWARF3 +// case DW_OP_bit_piece: // 0x9d DWARF3 2 +// s->Printf("DW_OP_bit_piece(0x%x, 0x%x)", m_data.GetULEB128(&offset), m_data.GetULEB128(&offset)); +// break; +// case DW_OP_lo_user: s->PutCString("DW_OP_lo_user"); break; // 0xe0 +// case DW_OP_hi_user: s->PutCString("DW_OP_hi_user"); break; // 0xff +// case DW_OP_APPLE_extern: +// s->Printf("DW_OP_APPLE_extern(%" PRIu64 ")", m_data.GetULEB128(&offset)); +// break; +// case DW_OP_APPLE_array_ref: +// s->PutCString("DW_OP_APPLE_array_ref"); +// break; + case DW_OP_APPLE_uninit: + s->PutCString("DW_OP_APPLE_uninit"); // 0xF0 + break; +// case DW_OP_APPLE_assign: // 0xF1 - pops value off and assigns it to second item on stack (2nd item must have assignable context) +// s->PutCString("DW_OP_APPLE_assign"); +// break; +// case DW_OP_APPLE_address_of: // 0xF2 - gets the address of the top stack item (top item must be a variable, or have value_type that is an address already) +// s->PutCString("DW_OP_APPLE_address_of"); +// break; +// case DW_OP_APPLE_value_of: // 0xF3 - pops the value off the stack and pushes the value of that object (top item must be a variable, or expression local) +// s->PutCString("DW_OP_APPLE_value_of"); +// break; +// case DW_OP_APPLE_deref_type: // 0xF4 - gets the address of the top stack item (top item must be a variable, or a clang type) +// s->PutCString("DW_OP_APPLE_deref_type"); +// break; +// case DW_OP_APPLE_expr_local: // 0xF5 - ULEB128 expression local index +// s->Printf("DW_OP_APPLE_expr_local(%" PRIu64 ")", m_data.GetULEB128(&offset)); +// break; +// case DW_OP_APPLE_constf: // 0xF6 - 1 byte float size, followed by constant float data +// { +// uint8_t float_length = m_data.GetU8(&offset); +// s->Printf("DW_OP_APPLE_constf(<%u> ", float_length); +// m_data.Dump(s, offset, eFormatHex, float_length, 1, UINT32_MAX, DW_INVALID_ADDRESS, 0, 0); +// s->PutChar(')'); +// // Consume the float data +// m_data.GetData(&offset, float_length); +// } +// break; +// case DW_OP_APPLE_scalar_cast: +// s->Printf("DW_OP_APPLE_scalar_cast(%s)", Scalar::GetValueTypeAsCString ((Scalar::Type)m_data.GetU8(&offset))); +// break; +// case DW_OP_APPLE_clang_cast: +// { +// clang::Type *clang_type = (clang::Type *)m_data.GetMaxU64(&offset, sizeof(void*)); +// s->Printf("DW_OP_APPLE_clang_cast(%p)", clang_type); +// } +// break; +// case DW_OP_APPLE_clear: +// s->PutCString("DW_OP_APPLE_clear"); +// break; +// case DW_OP_APPLE_error: // 0xFF - Stops expression evaluation and returns an error (no args) +// s->PutCString("DW_OP_APPLE_error"); +// 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)) + { + lldb::addr_t begin_addr_offset = m_data.GetAddress(&offset); + lldb::addr_t end_addr_offset = m_data.GetAddress(&offset); + 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 (begin_addr_offset == 0 && end_addr_offset == 0) + { + // The end of the location list is marked by both the start and end offset being zero + break; + } + 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, + uint32_t reg_kind, + uint32_t reg_num, + Error *error_ptr, + Value &value +) +{ + if (reg_ctx == NULL) + { + 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(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; +} + +//bool +//DWARFExpression::LocationListContainsLoadAddress (Process* process, const Address &addr) const +//{ +// return LocationListContainsLoadAddress(process, addr.GetLoadAddress(process)); +//} +// +//bool +//DWARFExpression::LocationListContainsLoadAddress (Process* process, addr_t load_addr) const +//{ +// if (load_addr == LLDB_INVALID_ADDRESS) +// return false; +// +// if (IsLocationList()) +// { +// lldb::offset_t offset = 0; +// +// addr_t loc_list_base_addr = m_loclist_slide.GetLoadAddress(process); +// +// if (loc_list_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 = m_data.GetAddress(&offset); +// addr_t hi_pc = m_data.GetAddress(&offset); +// if (lo_pc == 0 && hi_pc == 0) +// break; +// else +// { +// lo_pc += loc_list_base_addr; +// hi_pc += loc_list_base_addr; +// +// if (lo_pc <= load_addr && load_addr < hi_pc) +// return true; +// +// offset += m_data.GetU16(&offset); +// } +// } +// } +// 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 + 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_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 + 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 + { + 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 head_data_ap (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_ap->GetBytes(), + head_data_ap->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_ap.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::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 = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + break; + else + { + 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 = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + { + break; + } + else + { + 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, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + ExecutionContext exe_ctx (exe_scope); + return Evaluate(&exe_ctx, expr_locals, decl_map, NULL, loclist_base_load_addr, initial_value_ptr, result, error_ptr); +} + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + lldb::addr_t loclist_base_load_addr, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) const +{ + if (IsLocationList()) + { + lldb::offset_t offset = 0; + addr_t pc; + StackFrame *frame = NULL; + 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 = m_data.GetAddress(&offset); + addr_t hi_pc = m_data.GetAddress(&offset); + if (lo_pc == 0 && hi_pc == 0) + { + break; + } + else + { + 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, expr_locals, decl_map, reg_ctx, m_data, offset, length, m_reg_kind, initial_value_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, expr_locals, decl_map, reg_ctx, m_data, 0, m_data.GetByteSize(), m_reg_kind, initial_value_ptr, result, error_ptr); +} + + + +bool +DWARFExpression::Evaluate +( + ExecutionContext *exe_ctx, + ClangExpressionVariableList *expr_locals, + ClangExpressionDeclMap *decl_map, + RegisterContext *reg_ctx, + const DataExtractor& opcodes, + const lldb::offset_t opcodes_offset, + const lldb::offset_t opcodes_length, + const uint32_t reg_kind, + const Value* initial_value_ptr, + Value& result, + Error *error_ptr +) +{ + + if (opcodes_length == 0) + { + if (error_ptr) + error_ptr->SetErrorString ("no location, value may have been optimized out"); + return false; + } + std::vector stack; + + Process *process = NULL; + StackFrame *frame = NULL; + + if (exe_ctx) + { + process = exe_ctx->GetProcessPtr(); + frame = exe_ctx->GetFramePtr(); + } + if (reg_ctx == NULL && 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; + + // 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 %lu values:", count); + for (size_t i=0; iPrintf(" %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); + 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: + { + 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::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)]; + uint32_t addr_size = process->GetAddressByteSize(); + Error error; + if (process->ReadMemory(pointer_addr, &addr_bytes, addr_size, error) == addr_size) + { + DataExtractor addr_data(addr_bytes, sizeof(addr_bytes), process->GetByteOrder(), addr_size); + lldb::offset_t addr_data_offset = 0; + 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_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: + { + 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)]; + Error 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[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() == false) + { + 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() == false) + { + 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() == false) + { + 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().ResolveValue(exe_ctx) = stack.back().ResolveValue(exe_ctx) + tmp.ResolveValue(exe_ctx); + } + 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().ResolveValue(exe_ctx) += uconst_value; + if (!stack.back().ResolveValue(exe_ctx).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)) == false) + { + 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: + { + 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(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: + if (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_piece."); + return false; + + //---------------------------------------------------------------------- + // 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 (error_ptr) + error_ptr->SetErrorString ("Unimplemented opcode DW_OP_push_object_address."); + return false; + + //---------------------------------------------------------------------- + // 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::eValueTypeHostAddress); + } + 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; + default: + if (log) + log->Printf("Unhandled opcode %s in DWARFExpression.", DW_OP_value_to_name(op)); + break; + } + } + + if (stack.empty()) + { + 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 %lu values:", count); + for (size_t i=0; iPrintf(" %s", new_value.GetData()); + } + } + + result = stack.back(); + return true; // Return true on success +} + diff --git a/source/Expression/ExpressionSourceCode.cpp b/source/Expression/ExpressionSourceCode.cpp new file mode 100644 index 00000000000..aef3b9e301e --- /dev/null +++ b/source/Expression/ExpressionSourceCode.cpp @@ -0,0 +1,142 @@ +//===-- ExpressionSourceCode.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/ExpressionSourceCode.h" + +#include "lldb/Core/StreamString.h" + +using namespace lldb_private; + +const char * +ExpressionSourceCode::g_expression_prefix = R"( +#undef NULL +#undef Nil +#undef nil +#undef YES +#undef NO +#define NULL (__null) +#define Nil (__null) +#define nil (__null) +#define YES ((BOOL)1) +#define NO ((BOOL)0) +typedef signed char BOOL; +typedef signed __INT8_TYPE__ int8_t; +typedef unsigned __INT8_TYPE__ uint8_t; +typedef signed __INT16_TYPE__ int16_t; +typedef unsigned __INT16_TYPE__ uint16_t; +typedef signed __INT32_TYPE__ int32_t; +typedef unsigned __INT32_TYPE__ uint32_t; +typedef signed __INT64_TYPE__ int64_t; +typedef unsigned __INT64_TYPE__ uint64_t; +typedef signed __INTPTR_TYPE__ intptr_t; +typedef unsigned __INTPTR_TYPE__ uintptr_t; +typedef __SIZE_TYPE__ size_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef unsigned short unichar; +)"; + + +bool ExpressionSourceCode::GetText (std::string &text, lldb::LanguageType wrapping_language, bool const_object, bool static_method) const +{ + if (m_wrap) + { + switch (wrapping_language) + { + default: + return false; + case lldb::eLanguageTypeC: + case lldb::eLanguageTypeC_plus_plus: + case lldb::eLanguageTypeObjC: + break; + } + + StreamString wrap_stream; + + switch (wrapping_language) + { + default: + break; + case lldb::eLanguageTypeC: + wrap_stream.Printf("%s \n" + "%s \n" + "void \n" + "%s(void *$__lldb_arg) \n" + "{ \n" + " %s; \n" + "} \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_body.c_str()); + break; + case lldb::eLanguageTypeC_plus_plus: + wrap_stream.Printf("%s \n" + "%s \n" + "void \n" + "$__lldb_class::%s(void *$__lldb_arg) %s\n" + "{ \n" + " %s; \n" + "} \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + (const_object ? "const" : ""), + m_body.c_str()); + break; + case lldb::eLanguageTypeObjC: + if (static_method) + { + wrap_stream.Printf("%s \n" + "%s \n" + "@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" + "} \n" + "@end \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_name.c_str(), + m_body.c_str()); + } + else + { + wrap_stream.Printf("%s \n" + "%s \n" + "@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" + "} \n" + "@end \n", + g_expression_prefix, + m_prefix.c_str(), + m_name.c_str(), + m_name.c_str(), + m_body.c_str()); + } + break; + } + + text = wrap_stream.GetString(); + } + else + { + text.append(m_body); + } + + return true; +} diff --git a/source/Expression/IRDynamicChecks.cpp b/source/Expression/IRDynamicChecks.cpp new file mode 100644 index 00000000000..4030f149ab2 --- /dev/null +++ b/source/Expression/IRDynamicChecks.cpp @@ -0,0 +1,659 @@ +//===-- IRDynamicChecks.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRDynamicChecks.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/ClangUtilityFunction.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/ObjCLanguageRuntime.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/StackFrame.h" + +#include "llvm/Support/raw_ostream.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Value.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" +"}"; + +DynamicCheckerFunctions::DynamicCheckerFunctions () +{ +} + +DynamicCheckerFunctions::~DynamicCheckerFunctions () +{ +} + +bool +DynamicCheckerFunctions::Install(Stream &error_stream, + ExecutionContext &exe_ctx) +{ + m_valid_pointer_check.reset(new ClangUtilityFunction(g_valid_pointer_check_text, + VALID_POINTER_CHECK_NAME)); + if (!m_valid_pointer_check->Install(error_stream, exe_ctx)) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + + if (process) + { + ObjCLanguageRuntime *objc_language_runtime = process->GetObjCLanguageRuntime(); + + if (objc_language_runtime) + { + m_objc_object_check.reset(objc_language_runtime->CreateObjectChecker(VALID_OBJC_OBJECT_CHECK_NAME)); + + if (!m_objc_object_check->Install(error_stream, exe_ctx)) + return false; + } + } + + return true; +} + +bool +DynamicCheckerFunctions::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.get() != NULL && m_valid_pointer_check->ContainsAddress(addr)) + { + message.Printf ("Attempted to dereference an invalid pointer."); + return true; + } + else if (m_objc_object_check.get() != NULL && 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 +/// @brief 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, + DynamicCheckerFunctions &checker_functions) : + m_module(module), + m_checker_functions(checker_functions), + m_i8ptr_ty(NULL) + { + } + + virtual~Instrumenter () + { + } + + //------------------------------------------------------------------ + /// 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::Value *BuildPointerValidatorFunc(lldb::addr_t start_address) + { + IntegerType *intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + (m_module.getPointerSize() == llvm::Module::Pointer64) ? 64 : 32); + + llvm::Type *param_array[1]; + + param_array[0] = const_cast(GetI8PtrTy()); + + ArrayRef 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(intptr_ty, start_address, false); + return 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::Value *BuildObjectCheckerFunc(lldb::addr_t start_address) + { + IntegerType *intptr_ty = llvm::Type::getIntNTy(m_module.getContext(), + (m_module.getPointerSize() == llvm::Module::Pointer64) ? 64 : 32); + + llvm::Type *param_array[2]; + + param_array[0] = const_cast(GetI8PtrTy()); + param_array[1] = const_cast(GetI8PtrTy()); + + ArrayRef 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(intptr_ty, start_address, false); + return 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; + } + + typedef std::vector 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 + DynamicCheckerFunctions &m_checker_functions; ///< The dynamic checker functions for the process +private: + PointerType *m_i8ptr_ty; +}; + +class ValidPointerChecker : public Instrumenter +{ +public: + ValidPointerChecker (llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + Instrumenter(module, checker_functions), + m_valid_pointer_check_func(NULL) + { + } + + virtual ~ValidPointerChecker () + { + } +private: + bool InstrumentInstruction(llvm::Instruction *inst) + { + 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_functions.m_valid_pointer_check->StartAddress()); + + llvm::Value *dereferenced_ptr = NULL; + + if (llvm::LoadInst *li = dyn_cast (inst)) + dereferenced_ptr = li->getPointerOperand(); + else if (llvm::StoreInst *si = dyn_cast (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 args(arg_array, 1); + + CallInst::Create(m_valid_pointer_check_func, + args, + "", + inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) + { + if (dyn_cast (&i) || + dyn_cast (&i)) + RegisterInstruction(i); + + return true; + } + + llvm::Value *m_valid_pointer_check_func; +}; + +class ObjcObjectChecker : public Instrumenter +{ +public: + ObjcObjectChecker(llvm::Module &module, + DynamicCheckerFunctions &checker_functions) : + Instrumenter(module, checker_functions), + m_objc_object_check_func(NULL) + { + } + + virtual + ~ObjcObjectChecker () + { + } + + enum msgSend_type + { + eMsgSend = 0, + eMsgSendSuper, + eMsgSendSuper_stret, + eMsgSend_fpret, + eMsgSend_stret + }; + + std::map msgSend_types; + +private: + bool InstrumentInstruction(llvm::Instruction *inst) + { + CallInst *call_inst = dyn_cast(inst); + + if (!call_inst) + return false; // call_inst really shouldn't be NULL, because otherwise InspectInstruction wouldn't have registered it + + if (!m_objc_object_check_func) + m_objc_object_check_func = BuildObjectCheckerFunc(m_checker_functions.m_objc_object_check->StartAddress()); + + // id objc_msgSend(id theReceiver, SEL theSelector, ...) + + llvm::Value *target_object; + llvm::Value *selector; + + switch (msgSend_types[inst]) + { + case eMsgSend: + case eMsgSend_fpret: + 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 args(arg_array, 2); + + CallInst::Create(m_objc_object_check_func, + args, + "", + inst); + + return true; + } + + bool InspectInstruction(llvm::Instruction &i) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + CallInst *call_inst = dyn_cast(&i); + + if (call_inst) + { + // This metadata is set by IRForTarget::MaybeHandleCall(). + + MDNode *metadata = call_inst->getMetadata("lldb.call.realName"); + + if (!metadata) + return true; + + if (metadata->getNumOperands() != 1) + { + if (log) + log->Printf("Function call metadata has %d operands for [%p] %s", metadata->getNumOperands(), call_inst, PrintValue(call_inst).c_str()); + return false; + } + + MDString *real_name = dyn_cast(metadata->getOperand(0)); + + if (!real_name) + { + if (log) + log->Printf("Function call metadata is not an MDString for [%p] %s", call_inst, PrintValue(call_inst).c_str()); + return false; + } + + std::string name_str = real_name->getString(); + 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; + } + + llvm::Value *m_objc_object_check_func; +}; + +IRDynamicChecks::IRDynamicChecks(DynamicCheckerFunctions &checker_functions, + const char *func_name) : + ModulePass(ID), + m_func_name(func_name), + m_checker_functions(checker_functions) +{ +} + +IRDynamicChecks::~IRDynamicChecks() +{ +} + +bool +IRDynamicChecks::runOnModule(llvm::Module &M) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + llvm::Function* function = M.getFunction(StringRef(m_func_name.c_str())); + + 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.get()) + { + ValidPointerChecker vpc(M, m_checker_functions); + + if (!vpc.Inspect(*function)) + return false; + + if (!vpc.Instrument()) + return false; + } + + if (m_checker_functions.m_objc_object_check.get()) + { + ObjcObjectChecker ooc(M, m_checker_functions); + + 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, NULL); + + 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/source/Expression/IRExecutionUnit.cpp b/source/Expression/IRExecutionUnit.cpp new file mode 100644 index 00000000000..16ef6e5d54e --- /dev/null +++ b/source/Expression/IRExecutionUnit.cpp @@ -0,0 +1,704 @@ +//===-- IRExecutionUnit.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/SourceMgr.h" +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/Log.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" + +using namespace lldb_private; + +IRExecutionUnit::IRExecutionUnit (std::unique_ptr &context_ap, + std::unique_ptr &module_ap, + ConstString &name, + const lldb::TargetSP &target_sp, + std::vector &cpu_features) : + IRMemoryMap(target_sp), + m_context_ap(context_ap.release()), + m_module_ap(module_ap.release()), + m_module(m_module_ap.get()), + m_cpu_features(cpu_features), + m_name(name), + m_did_jit(false), + m_function_load_addr(LLDB_INVALID_ADDRESS), + m_function_end_load_addr(LLDB_INVALID_ADDRESS) +{ +} + +lldb::addr_t +IRExecutionUnit::WriteNow (const uint8_t *bytes, + size_t size, + Error &error) +{ + lldb::addr_t allocation_process_addr = Malloc (size, + 8, + lldb::ePermissionsWritable | lldb::ePermissionsReadable, + eAllocationPolicyMirror, + error); + + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + + WriteMemory(allocation_process_addr, bytes, size, error); + + if (!error.Success()) + { + Error err; + Free (allocation_process_addr, err); + + return LLDB_INVALID_ADDRESS; + } + + if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + DataBufferHeap my_buffer(size, 0); + Error 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); + + StreamString ss; + + my_extractor.Dump(&ss, 0, lldb::eFormatBytesWithASCII, 1, my_buffer.GetByteSize(), 32, allocation_process_addr, 0, 0); + + log->PutCString(ss.GetData()); + } + } + + return allocation_process_addr; +} + +void +IRExecutionUnit::FreeNow (lldb::addr_t allocation) +{ + if (allocation == LLDB_INVALID_ADDRESS) + return; + + Error err; + + Free(allocation, err); +} + +Error +IRExecutionUnit::DisassembleFunction (Stream &stream, + lldb::ProcessSP &process_wp) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + ExecutionContext exe_ctx(process_wp); + + Error 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 (strstr(function.m_name.c_str(), m_name.AsCString())) + { + 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 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(); + Error 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 = NULL; + const char *flavor_string = NULL; + 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(); + const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize(); + + for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize(); + instruction_index < num_instructions; + ++instruction_index) + { + Instruction *instruction = instruction_list.GetInstructionAtIndex(instruction_index).get(); + instruction->Dump (&stream, + max_opcode_byte_size, + true, + true, + &exe_ctx); + stream.PutChar('\n'); + } + // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. + // I'll fix that but for now, just clear the list and it will go away nicely. + disassembler_sp->GetInstructionList().Clear(); + return ret; +} + +static void ReportInlineAsmError(const llvm::SMDiagnostic &diagnostic, void *Context, unsigned LocCookie) +{ + Error *err = static_cast(Context); + + if (err && err->Success()) + { + err->SetErrorToGenericError(); + err->SetErrorStringWithFormat("Inline assembly error: %s", diagnostic.getMessage().str().c_str()); + } +} + +void +IRExecutionUnit::GetRunnableInfo(Error &error, + lldb::addr_t &func_addr, + lldb::addr_t &func_end) +{ + lldb::ProcessSP process_sp(GetProcessWP().lock()); + + 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; + }; + + 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, NULL); + + oss.flush(); + + log->Printf ("Module being sent to JIT: \n%s", s.c_str()); + } + + llvm::Triple triple(m_module->getTargetTriple()); + llvm::Function *function = m_module->getFunction (m_name.AsCString()); + llvm::Reloc::Model relocModel; + llvm::CodeModel::Model codeModel; + + if (triple.isOSBinFormatELF()) + { + relocModel = llvm::Reloc::Static; + // This will be small for 32-bit and large for 64-bit. + codeModel = llvm::CodeModel::JITDefault; + } + else + { + relocModel = llvm::Reloc::PIC_; + codeModel = llvm::CodeModel::Small; + } + + m_module_ap->getContext().setInlineAsmDiagnosticHandler(ReportInlineAsmError, &error); + + llvm::EngineBuilder builder(m_module_ap.get()); + + builder.setEngineKind(llvm::EngineKind::JIT) + .setErrorStr(&error_string) + .setRelocationModel(relocModel) + .setJITMemoryManager(new MemoryManager(*this)) + .setOptLevel(llvm::CodeGenOpt::Less) + .setAllocateGVsWithCode(true) + .setCodeModel(codeModel) + .setUseMCJIT(true); + + llvm::StringRef mArch; + llvm::StringRef mCPU; + llvm::SmallVector 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_ap.reset(builder.create(target_machine)); + + if (!m_execution_engine_ap.get()) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't JIT the function: %s", error_string.c_str()); + return; + } + else + { + m_module_ap.release(); // ownership was transferred + } + + m_execution_engine_ap->DisableLazyCompilation(); + + // We don't actually need the function pointer here, this just forces it to get resolved. + + void *fun_ptr = m_execution_engine_ap->getPointerToFunction(function); + + if (!error.Success()) + { + // We got an error through our callback! + return; + } + + if (!function) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("Couldn't find '%s' in the JITted module", m_name.AsCString()); + return; + } + + if (!fun_ptr) + { + error.SetErrorToGenericError(); + error.SetErrorStringWithFormat("'%s' was in the JITted module but wasn't lowered", m_name.AsCString()); + return; + } + + m_jitted_functions.push_back (JittedFunction(m_name.AsCString(), (lldb::addr_t)fun_ptr)); + + CommitAllocations(process_sp); + ReportAllocations(*m_execution_engine_ap); + WriteData(process_sp); + + for (JittedFunction &jitted_function : m_jitted_functions) + { + jitted_function.m_remote_addr = GetRemoteAddressForLocal (jitted_function.m_local_addr); + + if (!jitted_function.m_name.compare(m_name.AsCString())) + { + 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; + + Error 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()); + } + } + + func_addr = m_function_load_addr; + func_end = m_function_end_load_addr; + + return; +} + +IRExecutionUnit::~IRExecutionUnit () +{ + m_module_ap.reset(); + m_execution_engine_ap.reset(); + m_context_ap.reset(); +} + +IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) : + m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()), + m_parent (parent) +{ +} + +void +IRExecutionUnit::MemoryManager::setMemoryWritable () +{ + m_default_mm_ap->setMemoryWritable(); +} + +void +IRExecutionUnit::MemoryManager::setMemoryExecutable () +{ + m_default_mm_ap->setMemoryExecutable(); +} + + +uint8_t * +IRExecutionUnit::MemoryManager::startFunctionBody(const llvm::Function *F, + uintptr_t &ActualSize) +{ + return m_default_mm_ap->startFunctionBody(F, ActualSize); +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateStub(const llvm::GlobalValue* F, + unsigned StubSize, + unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + StubSize, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateStub (F=%p, StubSize=%u, Alignment=%u) = %p", + F, StubSize, Alignment, return_value); + } + + return return_value; +} + +void +IRExecutionUnit::MemoryManager::endFunctionBody(const llvm::Function *F, + uint8_t *FunctionStart, + uint8_t *FunctionEnd) +{ + m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd); +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateSpace(Size=%" PRIu64 ", Alignment=%u) = %p", + (uint64_t)Size, Alignment, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateCodeSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateCodeSection(Size, Alignment, SectionID); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsExecutable, + Size, + Alignment, + SectionID)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateDataSection(uintptr_t Size, + unsigned Alignment, + unsigned SectionID, + bool IsReadOnly) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateDataSection(Size, Alignment, SectionID, IsReadOnly); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment, + SectionID)); + if (log) + { + log->Printf("IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", + (uint64_t)Size, Alignment, SectionID, return_value); + } + + return return_value; +} + +uint8_t * +IRExecutionUnit::MemoryManager::allocateGlobal(uintptr_t Size, + unsigned Alignment) +{ + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment); + + m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + Size, + Alignment)); + + if (log) + { + log->Printf("IRExecutionUnit::allocateGlobal(Size=0x%" PRIx64 ", Alignment=%u) = %p", + (uint64_t)Size, Alignment, return_value); + } + + return return_value; +} + +void +IRExecutionUnit::MemoryManager::deallocateFunctionBody(void *Body) +{ + m_default_mm_ap->deallocateFunctionBody(Body); +} + +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::CommitAllocations (lldb::ProcessSP &process_sp) +{ + bool ret = true; + + lldb_private::Error err; + + for (AllocationRecord &record : m_records) + { + if (record.m_process_address != LLDB_INVALID_ADDRESS) + continue; + + + record.m_process_address = Malloc(record.m_size, + record.m_alignment, + record.m_permissions, + eAllocationPolicyProcessOnly, + err); + + if (!err.Success()) + { + ret = false; + 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) +{ + 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) +{ + for (AllocationRecord &record : m_records) + { + if (record.m_process_address == LLDB_INVALID_ADDRESS) + return false; + + lldb_private::Error err; + + WriteMemory (record.m_process_address, (uint8_t*)record.m_host_address, record.m_size, err); + } + + return true; +} + +void +IRExecutionUnit::AllocationRecord::dump (Log *log) +{ + if (!log) + return; + + log->Printf("[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d)", + (unsigned long long)m_host_address, + (unsigned long long)m_size, + (unsigned long long)m_process_address, + (unsigned)m_alignment, + (unsigned)m_section_id); +} diff --git a/source/Expression/IRForTarget.cpp b/source/Expression/IRForTarget.cpp new file mode 100644 index 00000000000..cac3fdf60df --- /dev/null +++ b/source/Expression/IRForTarget.cpp @@ -0,0 +1,2879 @@ +//===-- IRForTarget.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Expression/IRForTarget.h" + +#include "llvm/Support/raw_ostream.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/Module.h" +#include "llvm/PassManager.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/IR/ValueSymbolTable.h" + +#include "clang/AST/ASTContext.h" + +#include "lldb/Core/dwarf.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/ClangExpressionDeclMap.h" +#include "lldb/Expression/IRExecutionUnit.h" +#include "lldb/Expression/IRInterpreter.h" +#include "lldb/Host/Endian.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/ClangASTType.h" + +#include + +using namespace llvm; + +static char ID; + +IRForTarget::StaticDataAllocator::StaticDataAllocator(lldb_private::IRExecutionUnit &execution_unit) : + m_execution_unit(execution_unit), + m_stream_string(lldb_private::Stream::eBinary, execution_unit.GetAddressByteSize(), execution_unit.GetByteOrder()), + m_allocation(LLDB_INVALID_ADDRESS) +{ +} + +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]; +} + +lldb::addr_t IRForTarget::StaticDataAllocator::Allocate() +{ + lldb_private::Error err; + + if (m_allocation != LLDB_INVALID_ADDRESS) + { + m_execution_unit.FreeNow(m_allocation); + m_allocation = LLDB_INVALID_ADDRESS; + } + + m_allocation = m_execution_unit.WriteNow((const uint8_t*)m_stream_string.GetData(), m_stream_string.GetSize(), err); + + return m_allocation; +} + +static llvm::Value *FindEntryInstruction (llvm::Function *function) +{ + if (function->empty()) + return NULL; + + 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(NULL), + m_decl_map(decl_map), + m_data_allocator(execution_unit), + m_CFStringCreateWithBytes(NULL), + m_sel_registerName(NULL), + m_error_stream(error_stream), + m_result_store(NULL), + m_result_is_pointer(false), + m_reloc_placeholder(NULL), + 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); + + std::string name = llvm_function.getName().str(); + + return true; +} + +bool +IRForTarget::GetFunctionAddress (llvm::Function *fun, + uint64_t &fun_addr, + lldb_private::ConstString &name, + Constant **&value_ptr) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + fun_addr = LLDB_INVALID_ADDRESS; + name.Clear(); + value_ptr = NULL; + + if (fun->isIntrinsic()) + { + Intrinsic::ID intrinsic_id = (Intrinsic::ID)fun->getIntrinsicID(); + + switch (intrinsic_id) + { + default: + if (log) + log->Printf("Unresolved intrinsic \"%s\"", Intrinsic::getName(intrinsic_id).c_str()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Call to unhandled compiler intrinsic '%s'\n", Intrinsic::getName(intrinsic_id).c_str()); + + return false; + case Intrinsic::memcpy: + { + static lldb_private::ConstString g_memcpy_str ("memcpy"); + name = g_memcpy_str; + } + break; + case Intrinsic::memset: + { + static lldb_private::ConstString g_memset_str ("memset"); + name = g_memset_str; + } + break; + } + + if (log && name) + log->Printf("Resolved intrinsic name \"%s\"", name.GetCString()); + } + else + { + name.SetCStringWithLength (fun->getName().data(), fun->getName().size()); + } + + // Find the address of the function. + + clang::NamedDecl *fun_decl = DeclForGlobal (fun); + + if (fun_decl) + { + if (!m_decl_map->GetFunctionInfo (fun_decl, fun_addr)) + { + lldb_private::ConstString altnernate_name; + bool found_it = m_decl_map->GetFunctionAddress (name, fun_addr); + if (!found_it) + { + // Check for an alternate mangling for "std::basic_string" + // that is part of the itanium C++ name mangling scheme + const char *name_cstr = name.GetCString(); + if (name_cstr && strncmp(name_cstr, "_ZNKSbIcE", strlen("_ZNKSbIcE")) == 0) + { + std::string alternate_mangling("_ZNKSs"); + alternate_mangling.append (name_cstr + strlen("_ZNKSbIcE")); + altnernate_name.SetCString(alternate_mangling.c_str()); + found_it = m_decl_map->GetFunctionAddress (altnernate_name, fun_addr); + } + } + + if (!found_it) + { + lldb_private::Mangled mangled_name(name); + lldb_private::Mangled alt_mangled_name(altnernate_name); + if (log) + { + if (alt_mangled_name) + log->Printf("Function \"%s\" (alternate name \"%s\") has no address", + mangled_name.GetName().GetCString(), + alt_mangled_name.GetName().GetCString()); + else + log->Printf("Function \"%s\" had no address", + mangled_name.GetName().GetCString()); + } + + if (m_error_stream) + { + if (alt_mangled_name) + m_error_stream->Printf("error: call to a function '%s' (alternate name '%s') that is not present in the target\n", + mangled_name.GetName().GetCString(), + alt_mangled_name.GetName().GetCString()); + else if (mangled_name.GetMangledName()) + m_error_stream->Printf("error: call to a function '%s' ('%s') that is not present in the target\n", + mangled_name.GetName().GetCString(), + mangled_name.GetMangledName().GetCString()); + else + m_error_stream->Printf("error: call to a function '%s' that is not present in the target\n", + mangled_name.GetName().GetCString()); + } + return false; + } + } + } + else + { + if (!m_decl_map->GetFunctionAddress (name, fun_addr)) + { + if (log) + log->Printf ("Metadataless function \"%s\" had no address", name.GetCString()); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: Call to a symbol-only function '%s' that is not present in the target\n", name.GetCString()); + + return false; + } + } + + if (log) + log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), fun_addr); + + return true; +} + +llvm::Constant * +IRForTarget::BuildFunctionPointer (llvm::Type *type, + uint64_t ptr) +{ + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + PointerType *fun_ptr_ty = PointerType::getUnqual(type); + Constant *fun_addr_int = ConstantInt::get(intptr_ty, ptr, false); + return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); +} + +void +IRForTarget::RegisterFunctionMetadata(LLVMContext &context, + llvm::Value *function_ptr, + const char *name) +{ + for (Value::use_iterator i = function_ptr->use_begin(), e = function_ptr->use_end(); + i != e; + ++i) + { + Value *user = *i; + + if (Instruction *user_inst = dyn_cast(user)) + { + MDString* md_name = MDString::get(context, StringRef(name)); + + MDNode *metadata = MDNode::get(context, md_name); + + user_inst->setMetadata("lldb.call.realName", metadata); + } + else + { + RegisterFunctionMetadata (context, user, name); + } + } +} + +bool +IRForTarget::ResolveFunctionPointers(llvm::Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + for (llvm::Module::iterator fi = llvm_module.begin(); + fi != llvm_module.end(); + ++fi) + { + Function *fun = fi; + + bool is_decl = fun->isDeclaration(); + + if (log) + log->Printf("Examining %s function %s", (is_decl ? "declaration" : "non-declaration"), fun->getName().str().c_str()); + + if (!is_decl) + continue; + + if (fun->hasNUses(0)) + continue; // ignore + + uint64_t addr = LLDB_INVALID_ADDRESS; + lldb_private::ConstString name; + Constant **value_ptr = NULL; + + if (!GetFunctionAddress(fun, + addr, + name, + value_ptr)) + return false; // GetFunctionAddress reports its own errors + + Constant *value = BuildFunctionPointer(fun->getFunctionType(), addr); + + RegisterFunctionMetadata (llvm_module.getContext(), fun, name.AsCString()); + + if (value_ptr) + *value_ptr = value; + + // If we are replacing a function with the nobuiltin attribute, it may + // be called with the builtin attribute on call sites. Remove any such + // attributes since it's illegal to have a builtin call to something + // other than a nobuiltin function. + if (fun->hasFnAttribute(Attribute::NoBuiltin)) { + Attribute builtin = Attribute::get(fun->getContext(), Attribute::Builtin); + + for (auto u = fun->use_begin(), e = fun->use_end(); u != e; ++u) { + if (auto call = dyn_cast(*u)) { + call->removeAttribute(AttributeSet::FunctionIndex, builtin); + } + } + } + + fun->replaceAllUsesWith(value); + } + + 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 NULL; + + unsigned num_nodes = named_metadata->getNumOperands(); + unsigned node_index; + + for (node_index = 0; + node_index < num_nodes; + ++node_index) + { + MDNode *metadata_node = named_metadata->getOperand(node_index); + + if (!metadata_node) + return NULL; + + if (metadata_node->getNumOperands() != 2) + continue; + + if (metadata_node->getOperand(0) != global_val) + continue; + + ConstantInt *constant_int = dyn_cast(metadata_node->getOperand(1)); + + if (!constant_int) + return NULL; + + uintptr_t ptr = constant_int->getZExtValue(); + + return reinterpret_cast(ptr); + } + + return NULL; +} + +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 = NULL; + + 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"); + + if (m_error_stream) + 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(result_value); + + if (!result_global) + { + if (log) + log->PutCString("Result variable isn't a GlobalVariable"); + + if (m_error_stream) + 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"); + + if (m_error_stream) + 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(result_decl); + if (!result_var) + { + if (log) + log->PutCString("Result variable Decl isn't a VarDecl"); + + if (m_error_stream) + 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(); + const clang::ObjCObjectPointerType *pointer_objcobjpointertype = pointer_type->getAs(); + + if (pointer_pointertype) + { + clang::QualType element_qual_type = pointer_pointertype->getPointeeType(); + + m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), + &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(), + &result_decl->getASTContext()); + } + else + { + if (log) + log->PutCString("Expected result to have pointer type, but it did not"); + + if (m_error_stream) + 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(), + &result_decl->getASTContext()); + } + + if (m_result_type.GetBitSize() == 0) + { + lldb_private::StreamString type_desc_stream; + m_result_type.DumpTypeDescription(&type_desc_stream); + + if (log) + log->Printf("Result type has size 0"); + + if (m_error_stream) + 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()); + + // 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, + NULL, /* 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(result_decl), + false); + + llvm::Value* values[2]; + values[0] = new_result_global; + values[1] = new_constant_int; + + ArrayRef 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->hasNUses(0)) + { + // 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"); + + if (m_error_stream) + 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; +} + +#if 0 +static void DebugUsers(Log *log, Value *value, uint8_t depth) +{ + if (!depth) + return; + + depth--; + + if (log) + log->Printf(" ", value->getNumUses()); + + for (Value::use_iterator ui = value->use_begin(), ue = value->use_end(); + ui != ue; + ++ui) + { + if (log) + log->Printf(" %s", *ui, PrintValue(*ui).c_str()); + DebugUsers(log, *ui, depth); + } + + if (log) + log->Printf(" "); +} +#endif + +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()); + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() + == Module::Pointer64) ? 64 : 32); + 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"); + + if (!m_decl_map->GetFunctionAddress (g_CFStringCreateWithBytes_str, CFStringCreateWithBytes_addr)) + { + if (log) + log->PutCString("Couldn't find CFStringCreateWithBytes in the target"); + + if (m_error_stream) + 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] = intptr_ty; + arg_type_array[3] = i32_ty; + arg_type_array[4] = i8_ty; + + ArrayRef CFSCWB_arg_types(arg_type_array, 5); + + llvm::Type *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(intptr_ty, CFStringCreateWithBytes_addr, false); + m_CFStringCreateWithBytes = ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty); + } + + ConstantDataSequential *string_array = NULL; + + if (cstr) + string_array = dyn_cast(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(intptr_ty, cstr ? string_array->getNumElements() - 1 : 0, false); + Constant *encoding_arg = ConstantInt::get(i32_ty, 0x0600, false); /* 0x0600 is kCFStringEncodingASCII */ + 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 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(m_entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(ns_str, CFSCWB_Caller, m_entry_instruction_finder)) + { + if (log) + log->PutCString("Couldn't replace the NSString with the result of the call"); + + if (m_error_stream) + m_error_stream->Printf("Error [IRForTarget]: 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(nsstring_value); + + if (!nsstring_global) + { + if (log) + log->PutCString("NSString variable is not a GlobalVariable"); + + if (m_error_stream) + 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"); + + if (m_error_stream) + 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(nsstring_global->getInitializer()); + + if (!nsstring_struct) + { + if (log) + log->PutCString("NSString variable's initializer is not a ConstantStruct"); + + if (m_error_stream) + 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()); + + if (m_error_stream) + 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"); + + if (m_error_stream) + 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(nsstring_member); + + if (!nsstring_expr) + { + if (log) + log->PutCString("NSString initializer's str element is not a ConstantExpr"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not constant\n"); + + return false; + } + + if (nsstring_expr->getOpcode() != Instruction::GetElementPtr) + { + if (log) + log->Printf("NSString initializer's str element is not a GetElementPtr expression, it's a %s", nsstring_expr->getOpcodeName()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not an array\n"); + + return false; + } + + Constant *nsstring_cstr = nsstring_expr->getOperand(0); + + GlobalVariable *cstr_global = dyn_cast(nsstring_cstr); + + if (!cstr_global) + { + if (log) + log->PutCString("NSString initializer's str element is not a GlobalVariable"); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a global\n"); + + return false; + } + + if (!cstr_global->hasInitializer()) + { + if (log) + log->PutCString("NSString initializer's str element does not have an initializer"); + + if (m_error_stream) + 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(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 = NULL; + + 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(vi->second); + + if (!gv) + { + if (log) + log->PutCString("__CFConstantStringClassReference is not a global variable"); + + if (m_error_stream) + 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(value); + + if (!global_variable || !global_variable->hasName() || !global_variable->getName().startswith("\01L_OBJC_SELECTOR_REFERENCES_")) + return false; + + return true; +} + +// 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(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** @"\01L_OBJC_SELECTOR_REFERENCES_" ; + // %call = call i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; + // + // where %obj is the object pointer and %tmp is the selector. + // + // @"\01L_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(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(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(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(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; + + static lldb_private::ConstString g_sel_registerName_str ("sel_registerName"); + if (!m_decl_map->GetFunctionAddress (g_sel_registerName_str, sel_registerName_addr)) + 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 srN_arg_types(type_array, 1); + + llvm::Type *srN_type = FunctionType::get(sel_ptr_type, srN_arg_types, false); + + // Build the constant containing the pointer to the function + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type); + Constant *srN_addr_int = ConstantInt::get(intptr_ty, sel_registerName_addr, false); + m_sel_registerName = 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 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 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(&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)) + { + if (m_error_stream) + 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; +} + +// 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(persistent_alloc); + + MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr"); + + if (!alloc_md || !alloc_md->getNumOperands()) + return false; + + ConstantInt *constant_int = dyn_cast(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(ptr); + + lldb_private::TypeFromParser result_decl_type (decl->getType().getAsOpaquePtr(), + &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, + NULL, /* no initializer */ + alloc->getName().str().c_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::Value* values[2]; + values[0] = persistent_global; + values[1] = constant_int; + + ArrayRef 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 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(&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."); + + if (m_error_stream) + 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)) + { + if (m_error_stream) + 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)", data, PrintValue(initializer).c_str()); + + Type *initializer_type = initializer->getType(); + + if (ConstantInt *int_initializer = dyn_cast(initializer)) + { + memcpy (data, int_initializer->getValue().getRawData(), m_target_data->getTypeStoreSize(initializer_type)); + return true; + } + else if (ConstantDataArray *array_initializer = dyn_cast(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(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(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(initializer)) + { + memset(data, 0, m_target_data->getTypeStoreSize(initializer_type)); + return true; + } + return false; +} + +bool +IRForTarget::MaterializeInternalVariable (GlobalVariable *global_variable) +{ + if (GlobalVariable::isExternalLinkage(global_variable->getLinkage())) + return false; + + if (global_variable == m_reloc_placeholder) + return true; + + uint64_t offset = m_data_allocator.GetStream().GetSize(); + + llvm::Type *variable_type = global_variable->getType(); + + Constant *initializer = global_variable->getInitializer(); + + llvm::Type *initializer_type = initializer->getType(); + + size_t size = m_target_data->getTypeAllocSize(initializer_type); + size_t align = m_target_data->getPrefTypeAlignment(initializer_type); + + const size_t mask = (align - 1); + uint64_t aligned_offset = (offset + mask) & ~mask; + m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); + offset = aligned_offset; + + lldb_private::DataBufferHeap data(size, '\0'); + + if (initializer) + if (!MaterializeInitializer(data.GetBytes(), initializer)) + return false; + + m_data_allocator.GetStream().Write(data.GetBytes(), data.GetByteSize()); + + Constant *new_pointer = BuildRelocation(variable_type, offset); + + global_variable->replaceAllUsesWith(new_pointer); + + global_variable->eraseFromParent(); + + return true; +} + +// 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(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(llvm_value_ptr)) + { + if (!GlobalValue::isExternalLinkage(global_variable->getLinkage())) + return MaterializeInternalVariable(global_variable); + + 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(named_decl); + if (value_decl == NULL) + return false; + + lldb_private::ClangASTType clang_type(&value_decl->getASTContext(), value_decl->getType()); + + const Type *value_type = NULL; + + if (name[0] == '$') + { + // The $__lldb_expr_result name indicates the 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. + clang_type = clang_type.GetPointerType(); + value_type = PointerType::get(global_variable->getType(), 0); + } + else + { + value_type = global_variable->getType(); + } + + const uint64_t value_size = clang_type.GetByteSize(); + off_t value_alignment = (clang_type.GetTypeBitAlign() + 7ull) / 8ull; + + if (log) + { + log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64 ", align %" PRId64 "]", + name.c_str(), + clang_type.GetQualType().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 if (HandleSymbol (global_variable)) + return true; + else + return false; + } + } + else if (dyn_cast(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(); + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + Constant *symbol_addr_int = ConstantInt::get(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 + { + if (m_error_stream) + 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(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_begin() == global_variable->use_end()) + return false; + + SmallVector load_instructions; + + for (Value::use_iterator i = global_variable->use_begin(), e = global_variable->use_end(); + i != e; + ++i) + { + if (LoadInst *load_instruction = dyn_cast(*i)) + load_instructions.push_back(load_instruction); + } + + if (load_instructions.empty()) + return false; + + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() + == Module::Pointer64) ? 64 : 32); + + Constant *class_addr = ConstantInt::get(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 calls_to_remove; + + for (ii = basic_block.begin(); + ii != basic_block.end(); + ++ii) + { + Instruction &inst = *ii; + + CallInst *call = dyn_cast(&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::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(&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 (Module::global_iterator global = m_module->global_begin(), end = m_module->global_end(); + global != end; + ++global) + { + if (!global) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: global variable is NULL"); + + return false; + } + + std::string global_name = (*global).getName().str(); + + if (log) + log->Printf("Examining %s, DeclForGlobalValue returns %p", + global_name.c_str(), + DeclForGlobal(global)); + + if (global_name.find("OBJC_IVAR") == 0) + { + if (!HandleSymbol(global)) + { + if (m_error_stream) + 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)) + { + if (m_error_stream) + 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)) + { + if (m_error_stream) + 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)) + { + if (!MaybeHandleVariable (global)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite external variable %s\n", global_name.c_str()); + + return false; + } + } + } + + return true; +} + +bool +IRForTarget::ReplaceStrings () +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + typedef std::map OffsetsTy; + + OffsetsTy offsets; + + for (Module::global_iterator gi = m_module->global_begin(), ge = m_module->global_end(); + gi != ge; + ++gi) + { + GlobalVariable *gv = gi; + + if (!gv->hasInitializer()) + continue; + + Constant *gc = gv->getInitializer(); + + std::string str; + + if (gc->isNullValue()) + { + Type *gc_type = gc->getType(); + + ArrayType *gc_array_type = dyn_cast(gc_type); + + if (!gc_array_type) + continue; + + Type *gc_element_type = gc_array_type->getElementType(); + + IntegerType *gc_integer_type = dyn_cast(gc_element_type); + + if (gc_integer_type->getBitWidth() != 8) + continue; + + str = ""; + } + else + { + ConstantDataArray *gc_array = dyn_cast(gc); + + if (!gc_array) + continue; + + if (!gc_array->isCString()) + continue; + + if (log) + log->Printf("Found a GlobalVariable with string initializer %s", PrintValue(gc).c_str()); + + str = gc_array->getAsString(); + } + + offsets[gv] = m_data_allocator.GetStream().GetSize(); + + m_data_allocator.GetStream().Write(str.c_str(), str.length() + 1); + } + + Type *char_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); + + for (OffsetsTy::iterator oi = offsets.begin(), oe = offsets.end(); + oi != oe; + ++oi) + { + GlobalVariable *gv = oi->first; + size_t offset = oi->second; + + Constant *new_initializer = BuildRelocation(char_ptr_ty, offset); + + if (log) + log->Printf("Replacing GV %s with %s", PrintValue(gv).c_str(), PrintValue(new_initializer).c_str()); + + for (GlobalVariable::use_iterator ui = gv->use_begin(), ue = gv->use_end(); + ui != ue; + ++ui) + { + if (log) + log->Printf("Found use %s", PrintValue(*ui).c_str()); + + ConstantExpr *const_expr = dyn_cast(*ui); + StoreInst *store_inst = dyn_cast(*ui); + + if (const_expr) + { + if (const_expr->getOpcode() != Instruction::GetElementPtr) + { + if (log) + log->Printf("Use (%s) of string variable is not a GetElementPtr constant", PrintValue(const_expr).c_str()); + + return false; + } + + Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, const_expr->getOperand(0)->getType()); + Constant *new_gep = const_expr->getWithOperandReplaced(0, bit_cast); + + const_expr->replaceAllUsesWith(new_gep); + } + else if (store_inst) + { + Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, store_inst->getValueOperand()->getType()); + + store_inst->setOperand(0, bit_cast); + } + else + { + if (log) + log->Printf("Use (%s) of string variable is neither a constant nor a store", PrintValue(const_expr).c_str()); + + return false; + } + } + + gv->eraseFromParent(); + } + + return true; +} + +bool +IRForTarget::ReplaceStaticLiterals (llvm::BasicBlock &basic_block) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + typedef SmallVector ConstantList; + typedef SmallVector UserList; + typedef ConstantList::iterator ConstantIterator; + typedef UserList::iterator UserIterator; + + ConstantList static_constants; + UserList static_users; + + for (BasicBlock::iterator ii = basic_block.begin(), ie = basic_block.end(); + ii != ie; + ++ii) + { + llvm::Instruction &inst = *ii; + + for (Instruction::op_iterator oi = inst.op_begin(), oe = inst.op_end(); + oi != oe; + ++oi) + { + Value *operand_val = oi->get(); + + ConstantFP *operand_constant_fp = dyn_cast(operand_val); + + if (operand_constant_fp/* && operand_constant_fp->getType()->isX86_FP80Ty()*/) + { + static_constants.push_back(operand_val); + static_users.push_back(ii); + } + } + } + + ConstantIterator constant_iter; + UserIterator user_iter; + + for (constant_iter = static_constants.begin(), user_iter = static_users.begin(); + constant_iter != static_constants.end(); + ++constant_iter, ++user_iter) + { + Value *operand_val = *constant_iter; + llvm::Instruction *inst = *user_iter; + + ConstantFP *operand_constant_fp = dyn_cast(operand_val); + + if (operand_constant_fp) + { + Type *operand_type = operand_constant_fp->getType(); + + APFloat operand_apfloat = operand_constant_fp->getValueAPF(); + APInt operand_apint = operand_apfloat.bitcastToAPInt(); + + const uint8_t* operand_raw_data = (const uint8_t*)operand_apint.getRawData(); + size_t operand_data_size = operand_apint.getBitWidth() / 8; + + if (log) + { + std::string s; + raw_string_ostream ss(s); + for (size_t index = 0; + index < operand_data_size; + ++index) + { + ss << (uint32_t)operand_raw_data[index]; + ss << " "; + } + ss.flush(); + + log->Printf("Found ConstantFP with size %lu and raw data %s", operand_data_size, s.c_str()); + } + + lldb_private::DataBufferHeap data(operand_data_size, 0); + + if (lldb::endian::InlHostByteOrder() != m_data_allocator.GetStream().GetByteOrder()) + { + uint8_t *data_bytes = data.GetBytes(); + + for (size_t index = 0; + index < operand_data_size; + ++index) + { + data_bytes[index] = operand_raw_data[operand_data_size - (1 + index)]; + } + } + else + { + memcpy(data.GetBytes(), operand_raw_data, operand_data_size); + } + + uint64_t offset = m_data_allocator.GetStream().GetSize(); + + size_t align = m_target_data->getPrefTypeAlignment(operand_type); + + const size_t mask = (align - 1); + uint64_t aligned_offset = (offset + mask) & ~mask; + m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); + offset = aligned_offset; + + m_data_allocator.GetStream().Write(data.GetBytes(), operand_data_size); + + llvm::Type *fp_ptr_ty = operand_constant_fp->getType()->getPointerTo(); + + Constant *new_pointer = BuildRelocation(fp_ptr_ty, aligned_offset); + + llvm::LoadInst *fp_load = new llvm::LoadInst(new_pointer, "fp_load", inst); + + operand_constant_fp->replaceAllUsesWith(fp_load); + } + } + + return true; +} + +static bool isGuardVariableRef(Value *V) +{ + Constant *Old = NULL; + + if (!(Old = dyn_cast(V))) + return false; + + ConstantExpr *CE = NULL; + + if ((CE = dyn_cast(V))) + { + if (CE->getOpcode() != Instruction::BitCast) + return false; + + Old = CE->getOperand(0); + } + + GlobalVariable *GV = dyn_cast(Old); + + if (!GV || !GV->hasName() || !GV->getName().startswith("_ZGV")) + return false; + + return true; +} + +void +IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction* guard_load) +{ + Constant* zero(ConstantInt::get(Type::getInt8Ty(m_module->getContext()), 0, true)); + + Value::use_iterator ui; + + for (ui = guard_load->use_begin(); + ui != guard_load->use_end(); + ++ui) + { + if (isa(*ui)) + { + // do nothing for the moment + } + else + { + ui->replaceUsesOfWith(guard_load, 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 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(&inst)) + if (isGuardVariableRef(load->getPointerOperand())) + guard_loads.push_back(&inst); + + if (StoreInst *store = dyn_cast(&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, + FunctionValueCache &value_maker, + FunctionValueCache &entry_instruction_finder) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + Value::use_iterator ui; + + SmallVector users; + + // We do this because the use list might change, invalidating our iterator. + // Much better to keep a work list ourselves. + for (ui = old_constant->use_begin(); + ui != old_constant->use_end(); + ++ui) + users.push_back(*ui); + + for (size_t i = 0; + i < users.size(); + ++i) + { + User *user = users[i]; + + if (Constant *constant = dyn_cast(user)) + { + // synthesize a new non-constant equivalent of the constant + + if (ConstantExpr *constant_expr = dyn_cast(constant)) + { + switch (constant_expr->getOpcode()) + { + default: + if (log) + log->Printf("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(entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, bit_cast_maker, entry_instruction_finder)) + 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 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 indices(index_vector); + + return GetElementPtrInst::Create(ptr, indices, "", llvm::cast(entry_instruction_finder.GetValue(function))); + }); + + if (!UnfoldConstant(constant_expr, get_element_pointer_maker, entry_instruction_finder)) + return false; + } + break; + } + } + else + { + if (log) + log->Printf("Unhandled constant type: \"%s\"", PrintValue(constant).c_str()); + return false; + } + } + else + { + if (Instruction *inst = llvm::dyn_cast(user)) + { + inst->replaceUsesOfWith(old_constant, value_maker.GetValue(inst->getParent()->getParent())); + } + else + { + if (log) + log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(user).c_str()); + return false; + } + } + } + + if (!isa(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; + off_t alignment; + + if (!m_decl_map->GetStructInfo (num_elements, size, alignment)) + return false; + + Function::arg_iterator iter(llvm_function.getArgumentList().begin()); + + if (iter == llvm_function.getArgumentList().end()) + { + if (m_error_stream) + 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.getArgumentList().end()) + { + if (m_error_stream) + 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.getArgumentList().end()) + { + if (m_error_stream) + 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")) + { + if (m_error_stream) + 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.getArgumentList().end()) + { + if (m_error_stream) + 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")) + { + if (m_error_stream) + 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) + { + if (m_error_stream) + 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) + { + if (m_error_stream) + 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 = NULL; + Value *value = NULL; + off_t offset; + lldb_private::ConstString name; + + if (!m_decl_map->GetStructElement (decl, value, offset, name, element_index)) + { + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Structure information is incomplete"); + + return false; + } + + if (log) + log->Printf(" \"%s\" (\"%s\") placed at %" PRId64, + 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(m_entry_instruction_finder.GetValue(function)); + + ConstantInt *offset_int(ConstantInt::get(offset_type, offset, true)); + GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(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(value)) + { + UnfoldConstant(constant, body_result_maker, m_entry_instruction_finder); + } + else if (Instruction *instruction = dyn_cast(value)) + { + 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(value)) + var->eraseFromParent(); + } + } + + if (log) + log->Printf("Total structure [align %" PRId64 ", size %lu]", alignment, size); + + return true; +} + +llvm::Constant * +IRForTarget::BuildRelocation(llvm::Type *type, uint64_t offset) +{ + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + llvm::Constant *offset_int = ConstantInt::get(intptr_ty, offset); + + llvm::Constant *offset_array[1]; + + offset_array[0] = offset_int; + + llvm::ArrayRef offsets(offset_array, 1); + + llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(m_reloc_placeholder, offsets); + llvm::Constant *reloc_getbitcast = ConstantExpr::getBitCast(reloc_getelementptr, type); + + return reloc_getbitcast; +} + +bool +IRForTarget::CompleteDataAllocation () +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (!m_data_allocator.GetStream().GetSize()) + return true; + + lldb::addr_t allocation = m_data_allocator.Allocate(); + + if (log) + { + if (allocation) + log->Printf("Allocated static data at 0x%llx", (unsigned long long)allocation); + else + log->Printf("Failed to allocate static data"); + } + + if (!allocation || allocation == LLDB_INVALID_ADDRESS) + return false; + + IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), + (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); + + Constant *relocated_addr = ConstantInt::get(intptr_ty, (uint64_t)allocation); + Constant *relocated_bitcast = ConstantExpr::getIntToPtr(relocated_addr, llvm::Type::getInt8PtrTy(m_module->getContext())); + + m_reloc_placeholder->replaceAllUsesWith(relocated_bitcast); + + m_reloc_placeholder->eraseFromParent(); + + return true; +} + +bool +IRForTarget::StripAllGVs (Module &llvm_module) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + std::vector global_vars; + std::seterased_vars; + + bool erased = true; + + while (erased) + { + erased = false; + + for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); + gi != ge; + ++gi) + { + GlobalVariable *global_var = dyn_cast(gi); + + global_var->removeDeadConstantUsers(); + + if (global_var->use_empty()) + { + if (log) + log->Printf("Did remove %s", + PrintValue(global_var).c_str()); + global_var->eraseFromParent(); + erased = true; + break; + } + } + } + + for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); + gi != ge; + ++gi) + { + GlobalVariable *global_var = dyn_cast(gi); + + GlobalValue::use_iterator ui = global_var->use_begin(); + + if (log) + log->Printf("Couldn't remove %s because of %s", + PrintValue(global_var).c_str(), + PrintValue(*ui).c_str()); + } + + return true; +} + +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)); + + if (log) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + oss.flush(); + + log->Printf("Module as passed in to IRForTarget: \n\"%s\"", s.c_str()); + } + + Function* main_function = m_module->getFunction(StringRef(m_func_name.c_str())); + + if (!main_function) + { + if (log) + log->Printf("Couldn't find \"%s()\" in the module", m_func_name.c_str()); + + if (m_error_stream) + m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find wrapper '%s' in the module", m_func_name.c_str()); + + return false; + } + + if (!FixFunctionLinkage (*main_function)) + { + if (log) + log->Printf("Couldn't fix the linkage for the function"); + + return false; + } + + llvm::Type *intptr_ty = Type::getInt8Ty(m_module->getContext()); + + m_reloc_placeholder = new llvm::GlobalVariable((*m_module), + intptr_ty, + false /* IsConstant */, + GlobalVariable::InternalLinkage, + Constant::getNullValue(intptr_ty), + "reloc_placeholder", + NULL /* InsertBefore */, + GlobalVariable::NotThreadLocal /* ThreadLocal */, + 0 /* AddressSpace */); + + //////////////////////////////////////////////////////////// + // Replace $__lldb_expr_result with a persistent variable + // + + 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, NULL); + + 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; + } + + /////////////////////////////// + // Resolve function pointers + // + + if (!ResolveFunctionPointers(llvm_module)) + { + if (log) + log->Printf("ResolveFunctionPointers() failed"); + + // ResolveFunctionPointers() 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; + } + } + } + + 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; + } + + if (!ReplaceStaticLiterals(*bbi)) + { + if (log) + log->Printf("ReplaceStaticLiterals() failed"); + + return false; + } + } + } + + //////////////////////////////////////////////////////////////////////// + // Run function-level passes that only make sense on the 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 (!ReplaceStrings()) + { + if (log) + log->Printf("ReplaceStrings() failed"); + + return false; + } + + if (!CompleteDataAllocation()) + { + if (log) + log->Printf("CompleteDataAllocation() failed"); + + return false; + } + + if (!StripAllGVs(llvm_module)) + { + if (log) + log->Printf("StripAllGVs() failed"); + } + + if (log && log->GetVerbose()) + { + std::string s; + raw_string_ostream oss(s); + + m_module->print(oss, NULL); + + 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/source/Expression/IRInterpreter.cpp b/source/Expression/IRInterpreter.cpp new file mode 100644 index 00000000000..dcbf584cfb6 --- /dev/null +++ b/source/Expression/IRInterpreter.cpp @@ -0,0 +1,1379 @@ +//===-- IRInterpreter.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Expression/IRInterpreter.h" + +#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/Support/raw_ostream.h" + +#include + +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; +} + +class InterpreterStackFrame +{ +public: + typedef std::map ValueMap; + + ValueMap m_values; + DataLayout &m_target_data; + lldb_private::IRMemoryMap &m_memory_map; + const BasicBlock *m_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::IRMemoryMap &memory_map, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) : + m_target_data (target_data), + m_memory_map (memory_map) + { + 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_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); + + switch (type_size) + { + case 1: + scalar = (uint8_t)u64value; + break; + case 2: + scalar = (uint16_t)u64value; + break; + case 4: + scalar = (uint32_t)u64value; + break; + case 8: + scalar = (uint64_t)u64value; + break; + default: + return false; + } + + return true; + } + + bool EvaluateValue (lldb_private::Scalar &scalar, const Value *value, Module &module) + { + const Constant *constant = dyn_cast(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::Error extract_error; + + m_memory_map.GetMemoryData(value_extractor, process_address, value_size, extract_error); + + if (!extract_error.Success()) + return false; + + lldb::offset_t offset = 0; + if (value_size == 1 || value_size == 2 || value_size == 4 || 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.GetRawBits64(0), 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::Error get_data_error; + + if (!cast_scalar.GetAsMemoryData(buf.GetBytes(), buf.GetByteSize(), m_byte_order, get_data_error)) + return false; + + lldb_private::Error write_error; + + m_memory_map.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::ConstantIntVal: + if (const ConstantInt *constant_int = dyn_cast(constant)) + { + value = constant_int->getValue(); + return true; + } + break; + case Value::ConstantFPVal: + if (const ConstantFP *constant_fp = dyn_cast(constant)) + { + value = constant_fp->getValueAPF().bitcastToAPInt(); + return true; + } + break; + case Value::ConstantExprVal: + if (const ConstantExpr *constant_expr = dyn_cast(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(*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 indices (op_cursor, op_end); + + uint64_t offset = m_target_data.getIndexedOffset(base->getType(), indices); + + const bool is_signed = true; + value += APInt(value.getBitWidth(), offset, is_signed); + + return true; + } + } + } + break; + case Value::ConstantPointerNullVal: + if (isa(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::Error write_error; + + m_memory_map.WritePointerToMemory(data_address, address, write_error); + + if (!write_error.Success()) + { + lldb_private::Error free_error; + m_memory_map.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; + + const uint64_t *raw_data = resolved_value.getRawData(); + + size_t constant_size = m_target_data.getTypeStoreSize(constant->getType()); + + lldb_private::Error write_error; + + m_memory_map.WriteMemory(process_address, (uint8_t*)raw_data, constant_size, 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 MallocPointer () + { + return Malloc(m_target_data.getPointerSize(), m_target_data.getPointerPrefAlignment()); + } + + lldb::addr_t Malloc (llvm::Type *type) + { + lldb_private::Error 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::Error read_error; + + m_memory_map.ReadMemory(buf.GetBytes(), addr, length, read_error); + + if (!read_error.Success()) + return std::string(""); + + 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(value)) + { + if (!ResolveConstant (data_address, constant)) + { + lldb_private::Error free_error; + m_memory_map.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"; + +bool +IRInterpreter::CanInterpret (llvm::Module &module, + llvm::Function &function, + lldb_private::Error &error) +{ + 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) + 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::GetElementPtr: + break; + case Instruction::ICmp: + { + ICmpInst *icmp_inst = dyn_cast(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::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; + } + } + } + } + + } + + return true;} + +bool +IRInterpreter::Interpret (llvm::Module &module, + llvm::Function &function, + llvm::ArrayRef args, + lldb_private::IRMemoryMap &memory_map, + lldb_private::Error &error, + lldb::addr_t stack_frame_bottom, + lldb::addr_t stack_frame_top) +{ + lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + if (log) + { + std::string s; + raw_string_ostream oss(s); + + module.print(oss, NULL); + + oss.flush(); + + log->Printf("Module as passed in to IRInterpreter::Interpret: \n\"%s\"", s.c_str()); + } + + DataLayout data_layout(&module); + + InterpreterStackFrame frame(data_layout, memory_map, 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() < 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.begin()); + + 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(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: + result = L.GetRawBits64(0) / R.GetRawBits64(1); + break; + case Instruction::SRem: + L.MakeSigned(); + R.MakeSigned(); + result = L % R; + break; + case Instruction::URem: + result = L.GetRawBits64(0) % R.GetRawBits64(1); + 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(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::Error write_error; + + memory_map.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::Error free_error; + memory_map.Free(P, free_error); + memory_map.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(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(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(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.GetRawBits64(0)) + 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::GetElementPtr: + { + const GetElementPtrInst *gep_inst = dyn_cast(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 *pointer_type = pointer_operand->getType(); + + 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 IndexVector; + typedef IndexVector::iterator IndexIterator; + + SmallVector indices (gep_inst->idx_begin(), + gep_inst->idx_end()); + + SmallVector const_indices; + + for (IndexIterator ii = indices.begin(), ie = indices.end(); + ii != ie; + ++ii) + { + ConstantInt *constant_index = dyn_cast(*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::get((*ii)->getType(), I.ULongLong(LLDB_INVALID_ADDRESS))); + } + + const_indices.push_back(constant_index); + } + + uint64_t offset = data_layout.getIndexedOffset(pointer_type, 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(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: + result = (L.GetRawBits64(0) > R.GetRawBits64(0)); + break; + case CmpInst::ICMP_UGE: + result = (L.GetRawBits64(0) >= R.GetRawBits64(0)); + break; + case CmpInst::ICMP_ULT: + result = (L.GetRawBits64(0) < R.GetRawBits64(0)); + break; + case CmpInst::ICMP_ULE: + result = (L.GetRawBits64(0) <= R.GetRawBits64(0)); + 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(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(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::Load: + { + const LoadInst *load_inst = dyn_cast(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(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::Error read_error; + memory_map.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(); + memory_map.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::Error write_error; + memory_map.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(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(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::Error read_error; + memory_map.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(); + memory_map.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::Error write_error; + memory_map.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; + } + + ++frame.m_ii; + } + + if (num_insts >= 4096) + { + error.SetErrorToGenericError(); + error.SetErrorString(infinite_loop_error); + return false; + } + + return false; +} diff --git a/source/Expression/IRMemoryMap.cpp b/source/Expression/IRMemoryMap.cpp new file mode 100644 index 00000000000..ef362baff16 --- /dev/null +++ b/source/Expression/IRMemoryMap.cpp @@ -0,0 +1,758 @@ +//===-- IRMemoryMap.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Expression/IRMemoryMap.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.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; + + Error 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) +{ + lldb::TargetSP target_sp = m_target_wp.lock(); + lldb::ProcessSP process_sp = m_process_wp.lock(); + + lldb::addr_t ret = LLDB_INVALID_ADDRESS; + + if (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) + { + Error alloc_error; + + ret = process_sp->AllocateMemory(size, lldb::ePermissionsReadable | lldb::ePermissionsWritable, alloc_error); + + if (!alloc_error.Success()) + return LLDB_INVALID_ADDRESS; + else + return ret; + } + + for (int iterations = 0; iterations < 16; ++iterations) + { + lldb::addr_t candidate = LLDB_INVALID_ADDRESS; + + switch (target_sp->GetArchitecture().GetAddressByteSize()) + { + case 4: + { + uint32_t random_data = random(); + candidate = random_data; + candidate &= ~0xfffull; + break; + } + case 8: + { + uint32_t random_low = random(); + uint32_t random_high = random(); + candidate = random_high; + candidate <<= 32ull; + candidate |= random_low; + candidate &= ~0xfffull; + break; + } + } + + if (IntersectsAllocation(candidate, size)) + continue; + + ret = candidate; + + return ret; + } + + 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) +{ + if (addr == LLDB_INVALID_ADDRESS) + return false; + + AllocationMap::iterator iter = m_allocations.lower_bound (addr); + + if (iter == m_allocations.end() || + iter->first > addr) + { + if (iter == m_allocations.begin()) + return false; + + iter--; + } + + while (iter != m_allocations.end() && iter->second.m_process_alloc < addr + size) + { + if (iter->second.m_process_start + iter->second.m_size > addr) + return true; + + ++iter; + } + + return false; +} + +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() +{ + 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 NULL; +} + +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_permissions (permissions), + m_alignment (alignment), + m_policy (policy), + m_leak (false) +{ + switch (policy) + { + default: + assert (0 && "We cannot reach this!"); + case eAllocationPolicyHostOnly: + m_data.SetByteSize(size); + memset(m_data.GetBytes(), 0, size); + break; + case eAllocationPolicyProcessOnly: + break; + case eAllocationPolicyMirror: + m_data.SetByteSize(size); + memset(m_data.GetBytes(), 0, size); + break; + } +} + +lldb::addr_t +IRMemoryMap::Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error) +{ + error.Clear(); + + lldb::ProcessSP process_sp; + lldb::addr_t allocation_address = LLDB_INVALID_ADDRESS; + lldb::addr_t aligned_address = LLDB_INVALID_ADDRESS; + + size_t alignment_mask = alignment - 1; + size_t allocation_size; + + if (size == 0) + allocation_size = alignment; + else + allocation_size = (size & alignment_mask) ? ((size + alignment) & (~alignment_mask)) : size; + + 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 (process_sp && process_sp->CanJIT() && process_sp->IsAlive()) + { + allocation_address = process_sp->AllocateMemory(allocation_size, permissions, error); + if (!error.Success()) + return LLDB_INVALID_ADDRESS; + } + else + { + 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()) + { + allocation_address = process_sp->AllocateMemory(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[aligned_address] = Allocation(allocation_address, + aligned_address, + allocation_size, + permissions, + alignment, + policy); + + if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) + { + const char * policy_string; + + switch (policy) + { + default: + policy_string = ""; + 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, Error &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, Error &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); +} + +void +IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &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, Error &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, Error &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, Error &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; + + 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; + } + ::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, Error &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, Error &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, Error &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; + } + } + 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/source/Expression/Materializer.cpp b/source/Expression/Materializer.cpp new file mode 100644 index 00000000000..8a1900ebb73 --- /dev/null +++ b/source/Expression/Materializer.cpp @@ -0,0 +1,1414 @@ +//===-- Materializer.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Log.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectVariable.h" +#include "lldb/Expression/ClangExpressionVariable.h" +#include "lldb/Expression/Materializer.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" + +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 (ClangASTType &type) +{ + m_size = type.GetByteSize(); + + 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::ClangExpressionVariableSP &persistent_variable_sp) : + Entity(), + m_persistent_variable_sp(persistent_variable_sp) + { + // 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, Error &err) + { + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); + + // Allocate a spare memory area to store the persistent variable's contents. + + Error allocate_error; + + lldb::addr_t mem = map.Malloc(m_persistent_variable_sp->GetByteSize(), + 8, + lldb::ePermissionsReadable | lldb::ePermissionsWritable, + IRMemoryMap::eAllocationPolicyMirror, + 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 ") sucessfully", 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->GetTypeFromUser(), + m_persistent_variable_sp->GetName(), + mem, + eAddressTypeLoad, + m_persistent_variable_sp->GetByteSize()); + + // Clear the flag if the variable will never be deallocated. + + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget) + { + Error leak_error; + map.Leak(mem, leak_error); + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVNeedsAllocation; + } + + // Write the contents of the variable to the area. + + Error 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, Error &err) + { + Error 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, Error &err) + { + 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 & ClangExpressionVariable::EVNeedsAllocation) + { + MakeAllocation(map, err); + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + + if (!err.Success()) + return; + } + + if ((m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference && m_persistent_variable_sp->m_live_sp) || + m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsLLDBAllocated) + { + Error 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, + Error &err) + { + 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_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsLLDBAllocated) || + (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVIsProgramReference)) + { + if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::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; + Error 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->GetTypeFromUser(), + 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 |= ClangExpressionVariable::EVIsLLDBAllocated; + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + m_persistent_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsFreezeDry; + m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::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 & ClangExpressionVariable::EVNeedsFreezeDry || + m_persistent_variable_sp->m_flags & ClangExpressionVariable::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 (); + + Error 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 &= ~ClangExpressionVariable::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 |= ClangExpressionVariable::EVNeedsAllocation; + + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + else if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVNeedsAllocation && + !(m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget)) + { + DestroyAllocation(map, err); + if (!err.Success()) + return; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + Error 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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.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(" \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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, target_address); + + dump_stream.PutChar('\n'); + } + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + lldb::ClangExpressionVariableSP m_persistent_variable_sp; +}; + +uint32_t +Materializer::AddPersistentVariable (lldb::ClangExpressionVariableSP &persistent_variable_sp, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityPersistentVariable (persistent_variable_sp)); + 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()->GetClangForwardType().IsReferenceType(); + } + + void Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, Error &err) + { + 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; + } + + if (m_is_reference) + { + DataExtractor valobj_extractor; + valobj_sp->GetData(valobj_extractor); + lldb::offset_t offset = 0; + lldb::addr_t reference_addr = valobj_extractor.GetAddress(&offset); + + Error 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 + { + Error get_address_error; + lldb::ValueObjectSP addr_of_valobj_sp = valobj_sp->AddressOf(get_address_error); + if (get_address_error.Success()) + { + DataExtractor valobj_extractor; + addr_of_valobj_sp->GetData(valobj_extractor); + lldb::offset_t offset = 0; + lldb::addr_t addr_of_valobj_addr = valobj_extractor.GetAddress(&offset); + + Error write_error; + map.WritePointerToMemory(load_addr, addr_of_valobj_addr, 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; + valobj_sp->GetData(data); + + 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() == false) + { + 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 disagrees with the ValueObject's size", m_variable_sp->GetName().AsCString()); + } + return; + } + + size_t bit_align = m_variable_sp->GetType()->GetClangLayoutType().GetTypeBitAlign(); + size_t byte_align = (bit_align + 7) / 8; + + Error alloc_error; + + m_temporary_allocation = map.Malloc(data.GetByteSize(), byte_align, lldb::ePermissionsReadable | lldb::ePermissionsWritable, IRMemoryMap::eAllocationPolicyMirror, alloc_error); + m_temporary_allocation_size = 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; + } + + Error 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; + } + + Error 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, + Error &err) + { + 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; + + Error 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; + } + + Error set_error; + + 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; + } + + Error 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_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", load_addr); + + Error 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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.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(" \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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error 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; +}; + +uint32_t +Materializer::AddVariable (lldb::VariableSP &variable_sp, Error &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 TypeFromUser &type, bool is_program_reference, bool keep_in_memory) : + 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) + { + // 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, Error &err) + { + 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; + + size_t byte_size = m_type.GetByteSize(); + size_t bit_align = m_type.GetTypeBitAlign(); + size_t byte_align = (bit_align + 7) / 8; + + Error alloc_error; + + m_temporary_allocation = map.Malloc(byte_size, byte_align, lldb::ePermissionsReadable | lldb::ePermissionsWritable, IRMemoryMap::eAllocationPolicyMirror, 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; + } + + Error 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, + Error &err) + { + err.SetErrorString("Tried to detmaterialize a result variable with the normal Dematerialize method"); + } + + void Dematerialize (lldb::ClangExpressionVariableSP &result_variable_sp, + lldb::StackFrameSP &frame_sp, + IRMemoryMap &map, + lldb::addr_t process_address, + lldb::addr_t frame_top, + lldb::addr_t frame_bottom, + Error &err) + { + 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; + Error 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; + } + + ConstString name = target_sp->GetPersistentVariables().GetNextPersistentVariableName(); + + lldb::ClangExpressionVariableSP ret; + + ret = target_sp->GetPersistentVariables().CreateVariable(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(); + + 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, + ret->GetByteSize()); + } + + 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; + } + + result_variable_sp = ret; + + if (!can_persist || !m_keep_in_memory) + { + ret->m_flags |= ClangExpressionVariable::EVNeedsAllocation; + + if (m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + map.Free(m_temporary_allocation, free_error); + } + } + else + { + ret->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated; + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } + + void DumpToLog (IRMemoryMap &map, lldb::addr_t process_address, Log *log) + { + StreamString dump_stream; + + const lldb::addr_t load_addr = process_address + m_offset; + + dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", load_addr); + + Error 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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.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(" \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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + if (!m_keep_in_memory && m_temporary_allocation != LLDB_INVALID_ADDRESS) + { + Error free_error; + + map.Free(m_temporary_allocation, free_error); + } + + m_temporary_allocation = LLDB_INVALID_ADDRESS; + m_temporary_allocation_size = 0; + } +private: + TypeFromUser m_type; + bool m_is_program_reference; + bool m_keep_in_memory; + + lldb::addr_t m_temporary_allocation; + size_t m_temporary_allocation_size; +}; + +uint32_t +Materializer::AddResultVariable (const TypeFromUser &type, bool is_program_reference, bool keep_in_memory, Error &err) +{ + EntityVector::iterator iter = m_entities.insert(m_entities.end(), EntityUP()); + iter->reset (new EntityResultVariable (type, is_program_reference, keep_in_memory)); + uint32_t ret = AddStructMember(**iter); + (*iter)->SetOffset(ret); + m_result_entity = iter->get(); + 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, Error &err) + { + 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()); + } + + 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(); + + Error 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, + Error &err) + { + 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) + { + StreamString dump_stream; + + Error 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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + Symbol m_symbol; +}; + +uint32_t +Materializer::AddSymbol (const Symbol &symbol_sp, Error &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 ®ister_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, Error &err) + { + 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.reset(new DataBufferHeap(register_data.GetDataStart(), register_data.GetByteSize())); + + Error 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, + Error &err) + { + 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); + } + + Error 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(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) + { + StreamString dump_stream; + + Error 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(" \n"); + } + else + { + DataExtractor extractor (data.GetBytes(), data.GetByteSize(), map.GetByteOrder(), map.GetAddressByteSize()); + + extractor.DumpHexBytes(&dump_stream, data.GetBytes(), data.GetByteSize(), 16, load_addr); + + dump_stream.PutChar('\n'); + } + } + + log->PutCString(dump_stream.GetData()); + } + + void Wipe (IRMemoryMap &map, lldb::addr_t process_address) + { + } +private: + RegisterInfo m_register_info; + lldb::DataBufferSP m_register_contents; +}; + +uint32_t +Materializer::AddRegister (const RegisterInfo ®ister_info, Error &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_result_entity(NULL), + 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, Error &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:", 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 (Error &error, lldb::ClangExpressionVariableSP &result_sp, 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:", 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) + { + if (entity_up.get() == m_materializer->m_result_entity) + { + static_cast(m_materializer->m_result_entity)->Dematerialize (result_sp, frame_sp, *m_map, m_process_address, frame_top, frame_bottom, error); + } + else + { + 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 = NULL; + m_map = NULL; + m_process_address = LLDB_INVALID_ADDRESS; +} diff --git a/source/Host/common/Condition.cpp b/source/Host/common/Condition.cpp new file mode 100644 index 00000000000..daa729cadca --- /dev/null +++ b/source/Host/common/Condition.cpp @@ -0,0 +1,106 @@ +//===-- Condition.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "lldb/Host/Condition.h" +#include "lldb/Host/TimeValue.h" + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor +// +// The default constructor will initialize a new pthread condition +// and maintain the condition in the object state. +//---------------------------------------------------------------------- +Condition::Condition () : + m_condition() +{ + ::pthread_cond_init (&m_condition, NULL); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Destroys the pthread condition that the object owns. +//---------------------------------------------------------------------- +Condition::~Condition () +{ + ::pthread_cond_destroy (&m_condition); +} + +//---------------------------------------------------------------------- +// Unblock all threads waiting for a condition variable +//---------------------------------------------------------------------- +int +Condition::Broadcast () +{ + return ::pthread_cond_broadcast (&m_condition); +} + +//---------------------------------------------------------------------- +// Get accessor to the pthread condition object +//---------------------------------------------------------------------- +pthread_cond_t * +Condition::GetCondition () +{ + return &m_condition; +} + +//---------------------------------------------------------------------- +// Unblocks one thread waiting for the condition variable +//---------------------------------------------------------------------- +int +Condition::Signal () +{ + return ::pthread_cond_signal (&m_condition); +} + +//---------------------------------------------------------------------- +// The Wait() function atomically blocks the current thread +// waiting on the owned condition variable, and unblocks the mutex +// specified by "mutex". The waiting thread unblocks only after +// another thread calls Signal(), or Broadcast() with the same +// condition variable, or if "abstime" is valid (non-NULL) this +// function will return when the system time reaches the time +// specified in "abstime". If "abstime" is NULL this function will +// wait for an infinite amount of time for the condition variable +// to be signaled or broadcasted. +// +// The current thread re-acquires the lock on "mutex". +//---------------------------------------------------------------------- +int +Condition::Wait (Mutex &mutex, const TimeValue *abstime, bool *timed_out) +{ + int err = 0; + do + { + if (abstime && abstime->IsValid()) + { + struct timespec abstime_ts = abstime->GetAsTimeSpec(); + err = ::pthread_cond_timedwait (&m_condition, mutex.GetMutex(), &abstime_ts); + } + else + err = ::pthread_cond_wait (&m_condition, mutex.GetMutex()); + } while (err == EINTR); + + if (timed_out != NULL) + { + if (err == ETIMEDOUT) + *timed_out = true; + else + *timed_out = false; + } + + + return err; +} + diff --git a/source/Host/common/DynamicLibrary.cpp b/source/Host/common/DynamicLibrary.cpp new file mode 100644 index 00000000000..315a675895f --- /dev/null +++ b/source/Host/common/DynamicLibrary.cpp @@ -0,0 +1,33 @@ +//===-- DynamicLibrary.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/Error.h" +#include "lldb/Host/DynamicLibrary.h" + +using namespace lldb_private; + +DynamicLibrary::DynamicLibrary (const FileSpec& spec, uint32_t options) : m_filespec(spec) +{ + Error err; + m_handle = Host::DynamicLibraryOpen (spec,options,err); + if (err.Fail()) + m_handle = NULL; +} + +bool +DynamicLibrary::IsValid () +{ + return m_handle != NULL; +} + +DynamicLibrary::~DynamicLibrary () +{ + if (m_handle) + Host::DynamicLibraryClose (m_handle); +} diff --git a/source/Host/common/File.cpp b/source/Host/common/File.cpp new file mode 100644 index 00000000000..c0d3c290f4c --- /dev/null +++ b/source/Host/common/File.cpp @@ -0,0 +1,716 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include "lldb/Host/File.h" + +#include +#include +#include +#include +#include + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Error.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/FileSpec.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 NULL; +} + +int File::kInvalidDescriptor = -1; +FILE * File::kInvalidStream = NULL; + +File::File(const char *path, uint32_t options, uint32_t permissions) : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) +{ + Open (path, options, permissions); +} + +File::File (const File &rhs) : + m_descriptor (kInvalidDescriptor), + m_stream (kInvalidStream), + m_options (0), + m_owned (false) +{ + Duplicate (rhs); +} + + +File & +File::operator = (const File &rhs) +{ + if (this != &rhs) + Duplicate (rhs); + return *this; +} + +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()) + return fileno (m_stream); + + // Invalid descriptor and invalid stream, return invalid descriptor. + return kInvalidDescriptor; +} + +void +File::SetDescriptor (int fd, bool transfer_ownership) +{ + if (IsValid()) + Close(); + m_descriptor = fd; + m_owned = transfer_ownership; +} + + +FILE * +File::GetStream () +{ + if (!StreamIsValid()) + { + if (DescriptorIsValid()) + { + const char *mode = GetStreamOpenModeFromOptions (m_options); + if (mode) + { + do + { + m_stream = ::fdopen (m_descriptor, mode); + } while (m_stream == NULL && errno == EINTR); + } + } + } + return m_stream; +} + + +void +File::SetStream (FILE *fh, bool transfer_ownership) +{ + if (IsValid()) + Close(); + m_stream = fh; + m_owned = transfer_ownership; +} + +Error +File::Duplicate (const File &rhs) +{ + Error error; + if (IsValid ()) + Close(); + + if (rhs.DescriptorIsValid()) + { + m_descriptor = ::fcntl(rhs.GetDescriptor(), F_DUPFD); + if (!DescriptorIsValid()) + error.SetErrorToErrno(); + else + { + m_options = rhs.m_options; + m_owned = true; + } + } + else + { + error.SetErrorString ("invalid file to duplicate"); + } + return error; +} + +Error +File::Open (const char *path, uint32_t options, uint32_t permissions) +{ + Error error; + if (IsValid()) + Close (); + + int oflag = 0; + const bool read = options & eOpenOptionRead; + const bool write = options & eOpenOptionWrite; + if (write) + { + if (read) + oflag |= O_RDWR; + else + oflag |= O_WRONLY; + + if (options & eOpenOptionAppend) + oflag |= O_APPEND; + + if (options & eOpenOptionTruncate) + oflag |= O_TRUNC; + + if (options & eOpenOptionCanCreate) + oflag |= O_CREAT; + + if (options & eOpenOptionCanCreateNewOnly) + oflag |= O_CREAT | O_EXCL; + } + else if (read) + { + oflag |= O_RDONLY; + } + + if (options & eOpenOptionNonBlocking) + oflag |= O_NONBLOCK; + + mode_t mode = 0; + if (oflag & O_CREAT) + { + if (permissions & ePermissionsUserRead) mode |= S_IRUSR; + if (permissions & ePermissionsUserWrite) mode |= S_IWUSR; + if (permissions & ePermissionsUserExecute) mode |= S_IXUSR; + if (permissions & ePermissionsGroupRead) mode |= S_IRGRP; + if (permissions & ePermissionsGroupWrite) mode |= S_IWGRP; + if (permissions & ePermissionsGroupExecute) mode |= S_IXGRP; + if (permissions & ePermissionsWorldRead) mode |= S_IROTH; + if (permissions & ePermissionsWorldWrite) mode |= S_IWOTH; + if (permissions & ePermissionsWorldExecute) mode |= S_IXOTH; + } + + do + { + m_descriptor = ::open(path, oflag, mode); + } while (m_descriptor < 0 && errno == EINTR); + + if (!DescriptorIsValid()) + error.SetErrorToErrno(); + else + m_owned = true; + + return error; +} + +Error +File::Close () +{ + Error error; + if (IsValid ()) + { + if (m_owned) + { + if (StreamIsValid()) + { + if (::fclose (m_stream) == EOF) + error.SetErrorToErrno(); + } + + if (DescriptorIsValid()) + { + if (::close (m_descriptor) != 0) + error.SetErrorToErrno(); + } + } + m_descriptor = kInvalidDescriptor; + m_stream = kInvalidStream; + m_options = 0; + m_owned = false; + } + return error; +} + + +Error +File::GetFileSpec (FileSpec &file_spec) const +{ + Error error; +#ifdef LLDB_CONFIG_FCNTL_GETPATH_SUPPORTED + if (IsValid ()) + { + char path[PATH_MAX]; + if (::fcntl(GetDescriptor(), F_GETPATH, path) == -1) + error.SetErrorToErrno(); + else + file_spec.SetFile (path, false); + } + 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, false); + } + } +#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, Error *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, Error *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, Error *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; +} + +Error +File::Flush () +{ + Error error; + if (StreamIsValid()) + { + int err = 0; + do + { + err = ::fflush (m_stream); + } while (err == EOF && errno == EINTR); + + if (err == EOF) + error.SetErrorToErrno(); + } + else if (!DescriptorIsValid()) + { + error.SetErrorString("invalid file handle"); + } + return error; +} + + +Error +File::Sync () +{ + Error error; + if (DescriptorIsValid()) + { + int err = 0; + do + { + err = ::fsync (m_descriptor); + } while (err == -1 && errno == EINTR); + + if (err == -1) + error.SetErrorToErrno(); + } + else + { + error.SetErrorString("invalid file handle"); + } + return error; +} + +Error +File::Read (void *buf, size_t &num_bytes) +{ + Error error; + ssize_t bytes_read = -1; + if (DescriptorIsValid()) + { + do + { + bytes_read = ::read (m_descriptor, buf, num_bytes); + } while (bytes_read < 0 && errno == EINTR); + + 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; +} + +Error +File::Write (const void *buf, size_t &num_bytes) +{ + Error error; + ssize_t bytes_written = -1; + if (DescriptorIsValid()) + { + do + { + bytes_written = ::write (m_descriptor, buf, num_bytes); + } while (bytes_written < 0 && errno == EINTR); + + 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; +} + + +Error +File::Read (void *buf, size_t &num_bytes, off_t &offset) +{ + Error error; + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + ssize_t bytes_read = -1; + do + { + bytes_read = ::pread (fd, buf, num_bytes, offset); + } while (bytes_read < 0 && errno == EINTR); + + 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"); + } + return error; +} + +Error +File::Read (size_t &num_bytes, off_t &offset, bool null_terminate, DataBufferSP &data_buffer_sp) +{ + Error 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; + + std::unique_ptr data_heap_ap; + data_heap_ap.reset(new DataBufferHeap(num_bytes + (null_terminate ? 1 : 0), '\0')); + + if (data_heap_ap.get()) + { + error = Read (data_heap_ap->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 < data_heap_ap->GetByteSize()) + data_heap_ap->SetByteSize(num_bytes); + data_buffer_sp.reset(data_heap_ap.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; +} + +Error +File::Write (const void *buf, size_t &num_bytes, off_t &offset) +{ + Error error; + int fd = GetDescriptor(); + if (fd != kInvalidDescriptor) + { + ssize_t bytes_written = -1; + do + { + bytes_written = ::pwrite (m_descriptor, buf, num_bytes, offset); + } while (bytes_written < 0 && errno == EINTR); + + if (bytes_written < 0) + { + num_bytes = 0; + error.SetErrorToErrno(); + } + else + { + offset += bytes_written; + num_bytes = bytes_written; + } + } + 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 = NULL; + result = vasprintf(&s, format, args); + if (s != NULL) + { + 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; +} diff --git a/source/Host/common/FileSpec.cpp b/source/Host/common/FileSpec.cpp new file mode 100644 index 00000000000..08d626e836a --- /dev/null +++ b/source/Host/common/FileSpec.cpp @@ -0,0 +1,1059 @@ +//===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + + +#include +#include +#include +#include +#include +#include +#include + +#include "lldb/Host/Config.h" // Have to include this before we test the define... +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +#include +#endif + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" + +#include "lldb/Host/File.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataBufferMemoryMap.h" +#include "lldb/Core/RegularExpression.h" +#include "lldb/Core/Stream.h" +#include "lldb/Host/Host.h" +#include "lldb/Utility/CleanUp.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) +{ + char resolved_path[PATH_MAX]; + if (file_spec->GetPath (resolved_path, sizeof(resolved_path))) + return ::stat (resolved_path, stats_ptr) == 0; + return false; +} + +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + +static const char* +GetCachedGlobTildeSlash() +{ + static std::string g_tilde; + if (g_tilde.empty()) + { + struct passwd *user_entry; + user_entry = getpwuid(geteuid()); + if (user_entry != NULL) + g_tilde = user_entry->pw_dir; + + if (g_tilde.empty()) + return NULL; + } + return g_tilde.c_str(); +} + +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + +// Resolves the username part of a path of the form ~user/other/directories, and +// writes the result into dst_path. +// Returns 0 if there WAS a ~ in the path but the username couldn't be resolved. +// Otherwise returns the number of characters copied into dst_path. If the return +// is >= dst_len, then the resolved path is too long... +size_t +FileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len) +{ + if (src_path == NULL || src_path[0] == '\0') + return 0; + +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + + char user_home[PATH_MAX]; + const char *user_name; + + + // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...) + if (src_path[0] != '~') + { + size_t len = strlen (src_path); + if (len >= dst_len) + { + ::bcopy (src_path, dst_path, dst_len - 1); + dst_path[dst_len] = '\0'; + } + else + ::bcopy (src_path, dst_path, len + 1); + + return len; + } + + const char *first_slash = ::strchr (src_path, '/'); + char remainder[PATH_MAX]; + + if (first_slash == NULL) + { + // The whole name is the username (minus the ~): + user_name = src_path + 1; + remainder[0] = '\0'; + } + else + { + size_t user_name_len = first_slash - src_path - 1; + ::memcpy (user_home, src_path + 1, user_name_len); + user_home[user_name_len] = '\0'; + user_name = user_home; + + ::strcpy (remainder, first_slash); + } + + if (user_name == NULL) + return 0; + // User name of "" means the current user... + + struct passwd *user_entry; + const char *home_dir = NULL; + + if (user_name[0] == '\0') + { + home_dir = GetCachedGlobTildeSlash(); + } + else + { + user_entry = ::getpwnam (user_name); + if (user_entry != NULL) + home_dir = user_entry->pw_dir; + } + + if (home_dir == NULL) + return 0; + else + return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder); +#else + // Resolving home directories is not supported, just copy the path... + return ::snprintf (dst_path, dst_len, "%s", src_path); +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +} + +size_t +FileSpec::ResolvePartialUsername (const char *partial_name, StringList &matches) +{ +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + size_t extant_entries = matches.GetSize(); + + setpwent(); + struct passwd *user_entry; + const char *name_start = partial_name + 1; + std::set name_list; + + while ((user_entry = getpwent()) != NULL) + { + if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name) + { + std::string tmp_buf("~"); + tmp_buf.append(user_entry->pw_name); + tmp_buf.push_back('/'); + name_list.insert(tmp_buf); + } + } + std::set::iterator pos, end = name_list.end(); + for (pos = name_list.begin(); pos != end; pos++) + { + matches.AppendString((*pos).c_str()); + } + return matches.GetSize() - extant_entries; +#else + // Resolving home directories is not supported, just copy the path... + return 0; +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER +} + + + +size_t +FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len) +{ + if (src_path == NULL || src_path[0] == '\0') + return 0; + + // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path... + char unglobbed_path[PATH_MAX]; +#ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + if (src_path[0] == '~') + { + size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path)); + + // If we couldn't find the user referred to, or the resultant path was too long, + // then just copy over the src_path. + if (return_count == 0 || return_count >= sizeof(unglobbed_path)) + ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path); + } + else +#endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER + { + ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path); + } + + // Now resolve the path if needed + char resolved_path[PATH_MAX]; + if (::realpath (unglobbed_path, resolved_path)) + { + // Success, copy the resolved path + return ::snprintf(dst_path, dst_len, "%s", resolved_path); + } + else + { + // Failed, just copy the unglobbed path + return ::snprintf(dst_path, dst_len, "%s", unglobbed_path); + } +} + +FileSpec::FileSpec() : + m_directory(), + m_filename() +{ +} + +//------------------------------------------------------------------ +// Default constructor that can take an optional full path to a +// file on disk. +//------------------------------------------------------------------ +FileSpec::FileSpec(const char *pathname, bool resolve_path) : + m_directory(), + m_filename(), + m_is_resolved(false) +{ + if (pathname && pathname[0]) + SetFile(pathname, resolve_path); +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec& rhs) : + m_directory (rhs.m_directory), + m_filename (rhs.m_filename), + m_is_resolved (rhs.m_is_resolved) +{ +} + +//------------------------------------------------------------------ +// Copy constructor +//------------------------------------------------------------------ +FileSpec::FileSpec(const FileSpec* rhs) : + m_directory(), + m_filename() +{ + if (rhs) + *this = *rhs; +} + +//------------------------------------------------------------------ +// Virtual destrcuctor in case anyone inherits from this class. +//------------------------------------------------------------------ +FileSpec::~FileSpec() +{ +} + +//------------------------------------------------------------------ +// 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; + } + return *this; +} + +//------------------------------------------------------------------ +// 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 (const char *pathname, bool resolve) +{ + m_filename.Clear(); + m_directory.Clear(); + m_is_resolved = false; + if (pathname == NULL || pathname[0] == '\0') + return; + + char resolved_path[PATH_MAX]; + bool path_fit = true; + + if (resolve) + { + path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1); + m_is_resolved = path_fit; + } + else + { + // Copy the path because "basename" and "dirname" want to muck with the + // path buffer + if (::strlen (pathname) > sizeof(resolved_path) - 1) + path_fit = false; + else + ::strcpy (resolved_path, pathname); + } + + + if (path_fit) + { + char *filename = ::basename (resolved_path); + if (filename) + { + m_filename.SetCString (filename); + // Truncate the basename off the end of the resolved path + + // Only attempt to get the dirname if it looks like we have a path + if (strchr(resolved_path, '/')) + { + char *directory = ::dirname (resolved_path); + + // Make sure we didn't get our directory resolved to "." without having + // specified + if (directory) + m_directory.SetCString(directory); + else + { + char *last_resolved_path_slash = strrchr(resolved_path, '/'); + if (last_resolved_path_slash) + { + *last_resolved_path_slash = '\0'; + m_directory.SetCString(resolved_path); + } + } + } + } + else + m_directory.SetCString(resolved_path); + } +} + +//---------------------------------------------------------------------- +// 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; +} + +//------------------------------------------------------------------ +// Equal to operator +//------------------------------------------------------------------ +bool +FileSpec::operator== (const FileSpec& rhs) const +{ + if (m_filename == rhs.m_filename) + { + if (m_directory == rhs.m_directory) + return true; + + // TODO: determine if we want to keep this code in here. + // The code below was added to handle a case where we were + // trying to set a file and line breakpoint and one path + // was resolved, and the other not and the directory was + // in a mount point that resolved to a more complete path: + // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling + // this out... + if (IsResolved() && rhs.IsResolved()) + { + // Both paths are resolved, no need to look further... + return false; + } + + FileSpec resolved_lhs(*this); + + // If "this" isn't resolved, resolve it + if (!IsResolved()) + { + if (resolved_lhs.ResolvePath()) + { + // This path wasn't resolved but now it is. Check if the resolved + // directory is the same as our unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + m_is_resolved = (m_directory == resolved_lhs.m_directory); + } + else + return false; + } + + FileSpec resolved_rhs(rhs); + if (!rhs.IsResolved()) + { + if (resolved_rhs.ResolvePath()) + { + // rhs's path wasn't resolved but now it is. Check if the resolved + // directory is the same as rhs's unresolved directory, and if so, + // we can mark this object as resolved to avoid more future resolves + rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory); + } + else + return false; + } + + // If we reach this point in the code we were able to resolve both paths + // and since we only resolve the paths if the basenames are equal, then + // we can just check if both directories are equal... + return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory(); + } + return false; +} + +//------------------------------------------------------------------ +// 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; + + // 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); + if (result) + return result; + } + return ConstString::Compare (a.m_filename, b.m_filename); +} + +bool +FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) +{ + if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) + return a.m_filename == b.m_filename; + else + return a == b; +} + + + +//------------------------------------------------------------------ +// 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 +{ + static ConstString g_slash_only ("/"); + if (s) + { + m_directory.Dump(s); + if (m_directory && m_directory != g_slash_only) + s->PutChar('/'); + m_filename.Dump(s); + } +} + +//------------------------------------------------------------------ +// Returns true if the file exists. +//------------------------------------------------------------------ +bool +FileSpec::Exists () const +{ + struct stat file_stats; + return GetFileStats (this, &file_stats); +} + +bool +FileSpec::ResolveExecutableLocation () +{ + if (!m_directory) + { + const char *file_cstr = m_filename.GetCString(); + if (file_cstr) + { + const std::string file_str (file_cstr); + std::string path = llvm::sys::FindProgramByName (file_str); + llvm::StringRef dir_ref = llvm::sys::path::parent_path(path); + //llvm::StringRef dir_ref = path.getDirname(); + if (! dir_ref.empty()) + { + // FindProgramByName returns "." if it can't find the file. + if (strcmp (".", dir_ref.data()) == 0) + return false; + + m_directory.SetCString (dir_ref.data()); + if (Exists()) + return true; + else + { + // If FindProgramByName found the file, it returns the directory + filename in its return results. + // We need to separate them. + FileSpec tmp_file (dir_ref.data(), false); + if (tmp_file.Exists()) + { + m_directory = tmp_file.m_directory; + return true; + } + } + } + } + } + + return false; +} + +bool +FileSpec::ResolvePath () +{ + if (m_is_resolved) + return true; // We have already resolved this path + + char path_buf[PATH_MAX]; + if (!GetPath (path_buf, PATH_MAX)) + return false; + // SetFile(...) will set m_is_resolved correctly if it can resolve the path + SetFile (path_buf, true); + return m_is_resolved; +} + +uint64_t +FileSpec::GetByteSize() const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + return file_stats.st_size; + return 0; +} + +FileSpec::FileType +FileSpec::GetFileType () const +{ + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + { + mode_t file_type = file_stats.st_mode & S_IFMT; + switch (file_type) + { + case S_IFDIR: return eFileTypeDirectory; + case S_IFIFO: return eFileTypePipe; + case S_IFREG: return eFileTypeRegular; + case S_IFSOCK: return eFileTypeSocket; + case S_IFLNK: return eFileTypeSymbolicLink; + default: + break; + } + return eFileTypeUnknown; + } + return eFileTypeInvalid; +} + +TimeValue +FileSpec::GetModificationTime () const +{ + TimeValue mod_time; + struct stat file_stats; + if (GetFileStats (this, &file_stats)) + mod_time.OffsetWithSeconds(file_stats.st_mtime); + return mod_time; +} + +//------------------------------------------------------------------ +// Directory string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetDirectory() +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Directory string const get accessor. +//------------------------------------------------------------------ +const ConstString & +FileSpec::GetDirectory() const +{ + return m_directory; +} + +//------------------------------------------------------------------ +// Filename string get accessor. +//------------------------------------------------------------------ +ConstString & +FileSpec::GetFilename() +{ + return m_filename; +} + +//------------------------------------------------------------------ +// Filename string const get accessor. +//------------------------------------------------------------------ +const 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) const +{ + if (path_max_len) + { + const char *dirname = m_directory.GetCString(); + const char *filename = m_filename.GetCString(); + if (dirname) + { + if (filename) + return ::snprintf (path, path_max_len, "%s/%s", dirname, filename); + else + return ::snprintf (path, path_max_len, "%s", dirname); + } + else if (filename) + { + return ::snprintf (path, path_max_len, "%s", filename); + } + } + if (path) + path[0] = '\0'; + return 0; +} + +std::string +FileSpec::GetPath (void) const +{ + static ConstString g_slash_only ("/"); + std::string path; + const char *dirname = m_directory.GetCString(); + const char *filename = m_filename.GetCString(); + if (dirname) + { + path.append (dirname); + if (filename && m_directory != g_slash_only) + path.append ("/"); + } + if (filename) + path.append (filename); + return path; +} + +ConstString +FileSpec::GetFileNameExtension () const +{ + if (m_filename) + { + const char *filename = m_filename.GetCString(); + const char* dot_pos = strrchr(filename, '.'); + if (dot_pos && dot_pos[1] != '\0') + return ConstString(dot_pos+1); + } + return ConstString(); +} + +ConstString +FileSpec::GetFileNameStrippingExtension () const +{ + const char *filename = m_filename.GetCString(); + if (filename == NULL) + return ConstString(); + + const char* dot_pos = strrchr(filename, '.'); + if (dot_pos == NULL) + return m_filename; + + return ConstString(filename, dot_pos-filename); +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data is memory mapped and +// will lazily page in data from the file as memory is accessed. +// The data that is mappped will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const +{ + DataBufferSP data_sp; + std::unique_ptr mmap_data(new DataBufferMemoryMap()); + if (mmap_data.get()) + { + const size_t mapped_length = mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size); + if (((file_size == SIZE_MAX) && (mapped_length > 0)) || (mapped_length >= file_size)) + data_sp.reset(mmap_data.release()); + } + return data_sp; +} + + +//------------------------------------------------------------------ +// 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(); +} + + +size_t +FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const +{ + Error error; + size_t bytes_read = 0; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + off_t file_offset_after_seek = file_offset; + bytes_read = dst_len; + error = file.Read(dst, bytes_read, file_offset_after_seek); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return bytes_read; +} + +//------------------------------------------------------------------ +// Returns a shared pointer to a data buffer that contains all or +// part of the contents of a file. The data copies into a heap based +// buffer that lives in the DataBuffer shared pointer object returned. +// The data that is cached will start "file_offset" bytes into the +// file, and "file_size" bytes will be mapped. If "file_size" is +// greater than the number of bytes available in the file starting +// at "file_offset", the number of bytes will be appropriately +// truncated. The final number of bytes that get mapped can be +// verified using the DataBuffer::GetByteSize() function. +//------------------------------------------------------------------ +DataBufferSP +FileSpec::ReadFileContents (off_t file_offset, size_t file_size, Error *error_ptr) const +{ + Error error; + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + const bool null_terminate = false; + error = file.Read (file_size, file_offset, null_terminate, data_sp); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return data_sp; +} + +DataBufferSP +FileSpec::ReadFileContentsAsCString(Error *error_ptr) +{ + Error error; + DataBufferSP data_sp; + char resolved_path[PATH_MAX]; + if (GetPath(resolved_path, sizeof(resolved_path))) + { + File file; + error = file.Open(resolved_path, File::eOpenOptionRead); + if (error.Success()) + { + off_t offset = 0; + size_t length = SIZE_MAX; + const bool null_terminate = true; + error = file.Read (length, offset, null_terminate, data_sp); + } + } + else + { + error.SetErrorString("invalid file specification"); + } + if (error_ptr) + *error_ptr = error; + return data_sp; +} + +size_t +FileSpec::ReadFileLines (STLStringArray &lines) +{ + lines.clear(); + char path[PATH_MAX]; + if (GetPath(path, sizeof(path))) + { + std::ifstream file_stream (path); + + if (file_stream) + { + std::string line; + while (getline (file_stream, line)) + lines.push_back (line); + } + } + return lines.size(); +} + +FileSpec::EnumerateDirectoryResult +FileSpec::EnumerateDirectory +( + const char *dir_path, + bool find_directories, + bool find_files, + bool find_other, + EnumerateDirectoryCallbackType callback, + void *callback_baton +) +{ + if (dir_path && dir_path[0]) + { + lldb_utility::CleanUp dir_path_dir (opendir(dir_path), NULL, closedir); + if (dir_path_dir.is_valid()) + { + long path_max = fpathconf (dirfd (dir_path_dir.get()), _PC_NAME_MAX); +#if defined (__APPLE_) && defined (__DARWIN_MAXPATHLEN) + if (path_max < __DARWIN_MAXPATHLEN) + path_max = __DARWIN_MAXPATHLEN; +#endif + struct dirent *buf, *dp; + buf = (struct dirent *) malloc (offsetof (struct dirent, d_name) + path_max + 1); + + while (buf && readdir_r(dir_path_dir.get(), buf, &dp) == 0 && dp) + { + // Only search directories + if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) + { + size_t len = strlen(dp->d_name); + + if (len == 1 && dp->d_name[0] == '.') + continue; + + if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') + continue; + } + + bool call_callback = false; + FileSpec::FileType file_type = eFileTypeUnknown; + + switch (dp->d_type) + { + default: + case DT_UNKNOWN: file_type = eFileTypeUnknown; call_callback = true; break; + case DT_FIFO: file_type = eFileTypePipe; call_callback = find_other; break; + case DT_CHR: file_type = eFileTypeOther; call_callback = find_other; break; + case DT_DIR: file_type = eFileTypeDirectory; call_callback = find_directories; break; + case DT_BLK: file_type = eFileTypeOther; call_callback = find_other; break; + case DT_REG: file_type = eFileTypeRegular; call_callback = find_files; break; + case DT_LNK: file_type = eFileTypeSymbolicLink; call_callback = find_other; break; + case DT_SOCK: file_type = eFileTypeSocket; call_callback = find_other; break; +#if !defined(__OpenBSD__) + case DT_WHT: file_type = eFileTypeOther; call_callback = find_other; break; +#endif + } + + if (call_callback) + { + char child_path[PATH_MAX]; + const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); + if (child_path_len < (int)(sizeof(child_path) - 1)) + { + // Don't resolve the file type or path + FileSpec child_path_spec (child_path, false); + + EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); + + switch (result) + { + case eEnumerateDirectoryResultNext: + // Enumerate next entry in the current directory. We just + // exit this switch and will continue enumerating the + // current directory as we currently are... + break; + + case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not + if (FileSpec::EnumerateDirectory (child_path, + find_directories, + find_files, + find_other, + callback, + callback_baton) == eEnumerateDirectoryResultQuit) + { + // The subdirectory returned Quit, which means to + // stop all directory enumerations at all levels. + if (buf) + free (buf); + return eEnumerateDirectoryResultQuit; + } + break; + + case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. + // Exit from this directory level and tell parent to + // keep enumerating. + if (buf) + free (buf); + return eEnumerateDirectoryResultNext; + + case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level + if (buf) + free (buf); + return eEnumerateDirectoryResultQuit; + } + } + } + } + if (buf) + { + free (buf); + } + } + } + // By default when exiting a directory, we tell the parent enumeration + // to continue enumerating. + return eEnumerateDirectoryResultNext; +} + +//------------------------------------------------------------------ +/// 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) + { + static RegularExpression g_source_file_regex ("^(c|m|mm|cpp|c\\+\\+|cxx|cc|cp|s|asm|f|f77|f90|f95|f03|for|ftn|fpp|ada|adb|ads)$", + REG_EXTENDED | REG_ICASE); + return g_source_file_regex.Execute (extension.GetCString()); + } + return false; +} + +bool +FileSpec::IsRelativeToCurrentWorkingDirectory () const +{ + const char *directory = m_directory.GetCString(); + if (directory && directory[0]) + { + // If the path doesn't start with '/' or '~', return true + switch (directory[0]) + { + case '/': + case '~': + return false; + default: + return true; + } + } + else if (m_filename) + { + // No directory, just a basename, return true + return true; + } + return false; +} diff --git a/source/Host/common/Host.cpp b/source/Host/common/Host.cpp new file mode 100644 index 00000000000..a7bad0063ef --- /dev/null +++ b/source/Host/common/Host.cpp @@ -0,0 +1,1568 @@ +//===-- Host.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined (__APPLE__) + +#include +#include +#include +#include + +#endif + +#if defined (__linux__) || defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +#include +#include +#endif + +#if defined (__FreeBSD__) +#include +#endif + +#include "lldb/Host/Host.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ThreadSafeSTLMap.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/TargetList.h" + +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/raw_ostream.h" + + + + + +using namespace lldb; +using namespace lldb_private; + + +#if !defined (__APPLE__) +struct MonitorInfo +{ + lldb::pid_t pid; // The process ID to monitor + Host::MonitorChildProcessCallback callback; // The callback function to call when "pid" exits or signals + void *callback_baton; // The callback baton for the callback function + bool monitor_signals; // If true, call the callback when "pid" gets signaled. +}; + +static void * +MonitorChildProcessThreadFunction (void *arg); + +lldb::thread_t +Host::StartMonitoringChildProcess +( + Host::MonitorChildProcessCallback callback, + void *callback_baton, + lldb::pid_t pid, + bool monitor_signals +) +{ + lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; + MonitorInfo * info_ptr = new MonitorInfo(); + + info_ptr->pid = pid; + info_ptr->callback = callback; + info_ptr->callback_baton = callback_baton; + info_ptr->monitor_signals = monitor_signals; + + char thread_name[256]; + ::snprintf (thread_name, sizeof(thread_name), "", pid); + thread = ThreadCreate (thread_name, + MonitorChildProcessThreadFunction, + info_ptr, + NULL); + + return thread; +} + +//------------------------------------------------------------------ +// 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. +}; + +static void * +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; + void * const callback_baton = info->callback_baton; + const lldb::pid_t pid = info->pid; + const bool monitor_signals = info->monitor_signals; + + delete info; + + int status = -1; +#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) + #define __WALL 0 +#endif + const int options = __WALL; + + while (1) + { + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf("%s ::wait_pid (pid = %" PRIu64 ", &status, options = %i)...", function, pid, options); + + // Wait for all child processes + ::pthread_testcancel (); + // Get signals from all children with same process group of pid + const lldb::pid_t wait_pid = ::waitpid (-1*pid, &status, options); + ::pthread_testcancel (); + + if (wait_pid == -1) + { + if (errno == EINTR) + continue; + else + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because waitpid failed (%s)...", __FUNCTION__, arg, strerror(errno)); + break; + } + } + else if (wait_pid > 0) + { + bool exited = false; + int signal = 0; + int exit_status = 0; + const char *status_cstr = NULL; + 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 == pid) { + exited = true; + exit_status = -1; + } + } + else + { + status_cstr = "(\?\?\?)"; + } + + // Scope for pthread_cancel_disabler + { + ScopedPThreadCancelDisabler pthread_cancel_disabler; + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS); + if (log) + log->Printf ("%s ::waitpid (pid = %" PRIu64 ", &status, options = %i) => pid = %" PRIu64 ", status = 0x%8.8x (%s), signal = %i, exit_state = %i", + function, + wait_pid, + options, + pid, + status, + status_cstr, + signal, + exit_status); + + if (exited || (signal != 0 && monitor_signals)) + { + bool callback_return = false; + if (callback) + callback_return = callback (callback_baton, wait_pid, exited, signal, exit_status); + + // If our process exited, then this thread should exit + if (exited && wait_pid == 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 NULL; +} + + +void +Host::SystemLog (SystemLogType type, const char *format, va_list args) +{ + vfprintf (stderr, format, args); +} + +#endif // #if !defined (__APPLE__) + +void +Host::SystemLog (SystemLogType type, const char *format, ...) +{ + va_list args; + va_start (args, format); + SystemLog (type, format, args); + va_end (args); +} + +size_t +Host::GetPageSize() +{ + return ::getpagesize(); +} + +const ArchSpec & +Host::GetArchitecture (SystemDefaultArchitecture arch_kind) +{ + static bool g_supports_32 = false; + static bool g_supports_64 = false; + static ArchSpec g_host_arch_32; + static ArchSpec g_host_arch_64; + +#if defined (__APPLE__) + + // Apple is different in that it can support both 32 and 64 bit executables + // in the same operating system running concurrently. Here we detect the + // correct host architectures for both 32 and 64 bit including if 64 bit + // executables are supported on the system. + + if (g_supports_32 == false && g_supports_64 == false) + { + // All apple systems support 32 bit execution. + g_supports_32 = true; + uint32_t cputype, cpusubtype; + uint32_t is_64_bit_capable = false; + size_t len = sizeof(cputype); + ArchSpec host_arch; + // These will tell us about the kernel architecture, which even on a 64 + // bit machine can be 32 bit... + if (::sysctlbyname("hw.cputype", &cputype, &len, NULL, 0) == 0) + { + len = sizeof (cpusubtype); + if (::sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0) != 0) + cpusubtype = CPU_TYPE_ANY; + + len = sizeof (is_64_bit_capable); + if (::sysctlbyname("hw.cpu64bit_capable", &is_64_bit_capable, &len, NULL, 0) == 0) + { + if (is_64_bit_capable) + g_supports_64 = true; + } + + if (is_64_bit_capable) + { +#if defined (__i386__) || defined (__x86_64__) + if (cpusubtype == CPU_SUBTYPE_486) + cpusubtype = CPU_SUBTYPE_I386_ALL; +#endif + if (cputype & CPU_ARCH_ABI64) + { + // We have a 64 bit kernel on a 64 bit system + g_host_arch_32.SetArchitecture (eArchTypeMachO, ~(CPU_ARCH_MASK) & cputype, cpusubtype); + g_host_arch_64.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + } + else + { + // We have a 32 bit kernel on a 64 bit system + g_host_arch_32.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + cputype |= CPU_ARCH_ABI64; + g_host_arch_64.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + } + } + else + { + g_host_arch_32.SetArchitecture (eArchTypeMachO, cputype, cpusubtype); + g_host_arch_64.Clear(); + } + } + } + +#else // #if defined (__APPLE__) + + if (g_supports_32 == false && g_supports_64 == false) + { + llvm::Triple triple(llvm::sys::getDefaultTargetTriple()); + + g_host_arch_32.Clear(); + g_host_arch_64.Clear(); + + // If the OS is Linux, "unknown" in the vendor slot isn't what we want + // for the default triple. It's probably an artifact of config.guess. + if (triple.getOS() == llvm::Triple::Linux && triple.getVendor() == llvm::Triple::UnknownVendor) + triple.setVendorName(""); + + switch (triple.getArch()) + { + default: + g_host_arch_32.SetTriple(triple); + g_supports_32 = true; + break; + + case llvm::Triple::x86_64: + g_host_arch_64.SetTriple(triple); + g_supports_64 = true; + g_host_arch_32.SetTriple(triple.get32BitArchVariant()); + g_supports_32 = true; + break; + + case llvm::Triple::sparcv9: + case llvm::Triple::ppc64: + g_host_arch_64.SetTriple(triple); + g_supports_64 = true; + break; + } + + g_supports_32 = g_host_arch_32.IsValid(); + g_supports_64 = g_host_arch_64.IsValid(); + } + +#endif // #else for #if defined (__APPLE__) + + if (arch_kind == eSystemDefaultArchitecture32) + return g_host_arch_32; + else if (arch_kind == eSystemDefaultArchitecture64) + return g_host_arch_64; + + if (g_supports_64) + return g_host_arch_64; + + return g_host_arch_32; +} + +const ConstString & +Host::GetVendorString() +{ + static ConstString g_vendor; + if (!g_vendor) + { + const ArchSpec &host_arch = GetArchitecture (eSystemDefaultArchitecture); + const llvm::StringRef &str_ref = host_arch.GetTriple().getVendorName(); + g_vendor.SetCStringWithLength(str_ref.data(), str_ref.size()); + } + return g_vendor; +} + +const ConstString & +Host::GetOSString() +{ + static ConstString g_os_string; + if (!g_os_string) + { + const ArchSpec &host_arch = GetArchitecture (eSystemDefaultArchitecture); + const llvm::StringRef &str_ref = host_arch.GetTriple().getOSName(); + g_os_string.SetCStringWithLength(str_ref.data(), str_ref.size()); + } + return g_os_string; +} + +const ConstString & +Host::GetTargetTriple() +{ + static ConstString g_host_triple; + if (!(g_host_triple)) + { + const ArchSpec &host_arch = GetArchitecture (eSystemDefaultArchitecture); + g_host_triple.SetCString(host_arch.GetTriple().getTriple().c_str()); + } + return g_host_triple; +} + +lldb::pid_t +Host::GetCurrentProcessID() +{ + return ::getpid(); +} + +lldb::tid_t +Host::GetCurrentThreadID() +{ +#if defined (__APPLE__) + // Calling "mach_port_deallocate()" bumps the reference count on the thread + // port, so we need to deallocate it. mach_task_self() doesn't bump the ref + // count. + thread_port_t thread_self = mach_thread_self(); + mach_port_deallocate(mach_task_self(), thread_self); + return thread_self; +#elif defined(__FreeBSD__) + return lldb::tid_t(pthread_getthreadid_np()); +#elif defined(__linux__) + return lldb::tid_t(syscall(SYS_gettid)); +#else + return lldb::tid_t(pthread_self()); +#endif +} + +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(_POSIX_C_SOURCE) && !defined(_DARWIN_C_SOURCE)) + case SIGPOLL: return "SIGPOLL"; // 7 pollable event ([XSR] generated, not supported) +#endif +#if !defined(_POSIX_C_SOURCE) + 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<OSTOP) +#if !defined(_POSIX_C_SOURCE) + 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(_POSIX_C_SOURCE) + case SIGWINCH: return "SIGWINCH"; // 28 window size changes + 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 NULL; +} + +void +Host::WillTerminate () +{ +} + +#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) // see macosx/Host.mm + +void +Host::ThreadCreated (const char *thread_name) +{ +} + +void +Host::Backtrace (Stream &strm, uint32_t max_frames) +{ + // TODO: Is there a way to backtrace the current process on other systems? +} + +size_t +Host::GetEnvironment (StringList &env) +{ + // TODO: Is there a way to the host environment for this process on other systems? + return 0; +} + +#endif // #if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined (__linux__) + +struct HostThreadCreateInfo +{ + std::string thread_name; + thread_func_t thread_fptr; + thread_arg_t thread_arg; + + HostThreadCreateInfo (const char *name, thread_func_t fptr, thread_arg_t arg) : + thread_name (name ? name : ""), + thread_fptr (fptr), + thread_arg (arg) + { + } +}; + +static thread_result_t +ThreadCreateTrampoline (thread_arg_t arg) +{ + HostThreadCreateInfo *info = (HostThreadCreateInfo *)arg; + Host::ThreadCreated (info->thread_name.c_str()); + 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); +} + +lldb::thread_t +Host::ThreadCreate +( + const char *thread_name, + thread_func_t thread_fptr, + thread_arg_t thread_arg, + Error *error +) +{ + lldb::thread_t thread = LLDB_INVALID_HOST_THREAD; + + // Host::ThreadCreateTrampoline will delete this pointer for us. + HostThreadCreateInfo *info_ptr = new HostThreadCreateInfo (thread_name, thread_fptr, thread_arg); + + int err = ::pthread_create (&thread, NULL, ThreadCreateTrampoline, info_ptr); + if (err == 0) + { + if (error) + error->Clear(); + return thread; + } + + if (error) + error->SetError (err, eErrorTypePOSIX); + + return LLDB_INVALID_HOST_THREAD; +} + +bool +Host::ThreadCancel (lldb::thread_t thread, Error *error) +{ + int err = ::pthread_cancel (thread); + if (error) + error->SetError(err, eErrorTypePOSIX); + return err == 0; +} + +bool +Host::ThreadDetach (lldb::thread_t thread, Error *error) +{ + int err = ::pthread_detach (thread); + if (error) + error->SetError(err, eErrorTypePOSIX); + return err == 0; +} + +bool +Host::ThreadJoin (lldb::thread_t thread, thread_result_t *thread_result_ptr, Error *error) +{ + int err = ::pthread_join (thread, thread_result_ptr); + if (error) + error->SetError(err, eErrorTypePOSIX); + return err == 0; +} + +bool +Host::SetThreadName (lldb::pid_t pid, lldb::tid_t tid, const char *name) +{ +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5 + lldb::pid_t curr_pid = Host::GetCurrentProcessID(); + lldb::tid_t curr_tid = Host::GetCurrentThreadID(); + if (pid == LLDB_INVALID_PROCESS_ID) + pid = curr_pid; + + if (tid == LLDB_INVALID_THREAD_ID) + tid = curr_tid; + + // Set the pthread name if possible + if (pid == curr_pid && tid == curr_tid) + { + if (::pthread_setname_np (name) == 0) + return true; + } + return false; +#elif defined (__FreeBSD__) + lldb::pid_t curr_pid = Host::GetCurrentProcessID(); + lldb::tid_t curr_tid = Host::GetCurrentThreadID(); + if (pid == LLDB_INVALID_PROCESS_ID) + pid = curr_pid; + + if (tid == LLDB_INVALID_THREAD_ID) + tid = curr_tid; + + // Set the pthread name if possible + if (pid == curr_pid && tid == curr_tid) + { + ::pthread_set_name_np (::pthread_self(), name); + return true; + } + return false; +#elif defined (__linux__) || defined (__GLIBC__) + void *fn = dlsym (RTLD_DEFAULT, "pthread_setname_np"); + if (fn) + { + lldb::pid_t curr_pid = Host::GetCurrentProcessID(); + lldb::tid_t curr_tid = Host::GetCurrentThreadID(); + if (pid == LLDB_INVALID_PROCESS_ID) + pid = curr_pid; + + if (tid == LLDB_INVALID_THREAD_ID) + tid = curr_tid; + + if (pid == curr_pid && tid == curr_tid) + { + int (*pthread_setname_np_func)(pthread_t thread, const char *name); + *reinterpret_cast (&pthread_setname_np_func) = fn; + + if (pthread_setname_np_func (::pthread_self(), name) == 0) + return true; + } + } + return false; +#else + return false; +#endif +} + +bool +Host::SetShortThreadName (lldb::pid_t pid, lldb::tid_t tid, + const char *thread_name, size_t len) +{ + char *namebuf = (char *)::malloc (len + 1); + + // Thread names are coming in like '' and + // ''. So just chopping the end of the string + // off leads to a lot of similar named threads. Go through the thread name + // and search for the last dot and use that. + const char *lastdot = ::strrchr (thread_name, '.'); + + if (lastdot && lastdot != thread_name) + thread_name = lastdot + 1; + ::strncpy (namebuf, thread_name, len); + namebuf[len] = 0; + + int namebuflen = strlen(namebuf); + if (namebuflen > 0) + { + if (namebuf[namebuflen - 1] == '(' || namebuf[namebuflen - 1] == '>') + { + // Trim off trailing '(' and '>' characters for a bit more cleanup. + namebuflen--; + namebuf[namebuflen] = 0; + } + return Host::SetThreadName (pid, tid, namebuf); + } + return false; +} + +FileSpec +Host::GetProgramFileSpec () +{ + static FileSpec g_program_filespec; + if (!g_program_filespec) + { +#if defined (__APPLE__) + char program_fullpath[PATH_MAX]; + // If DST is NULL, then return the number of bytes needed. + uint32_t len = sizeof(program_fullpath); + int err = _NSGetExecutablePath (program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile (program_fullpath, false); + else if (err == -1) + { + char *large_program_fullpath = (char *)::malloc (len + 1); + + err = _NSGetExecutablePath (large_program_fullpath, &len); + if (err == 0) + g_program_filespec.SetFile (large_program_fullpath, false); + + ::free (large_program_fullpath); + } +#elif defined (__linux__) + char exe_path[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1); + if (len > 0) { + exe_path[len] = 0; + g_program_filespec.SetFile(exe_path, false); + } +#elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) + 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, false); + delete[] exe_path; + } +#endif + } + return g_program_filespec; +} + +FileSpec +Host::GetModuleFileSpecForHostAddress (const void *host_addr) +{ + FileSpec module_filespec; + Dl_info info; + if (::dladdr (host_addr, &info)) + { + if (info.dli_fname) + module_filespec.SetFile(info.dli_fname, true); + } + return module_filespec; +} + +#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 + +// Opaque info that tracks a dynamic library that was loaded +struct DynamicLibraryInfo +{ + DynamicLibraryInfo (const FileSpec &fs, int o, void *h) : + file_spec (fs), + open_options (o), + handle (h) + { + } + + const FileSpec file_spec; + uint32_t open_options; + void * handle; +}; + +void * +Host::DynamicLibraryOpen (const FileSpec &file_spec, uint32_t options, Error &error) +{ + char path[PATH_MAX]; + if (file_spec.GetPath(path, sizeof(path))) + { + int mode = 0; + + if (options & eDynamicLibraryOpenOptionLazy) + mode |= RTLD_LAZY; + else + mode |= RTLD_NOW; + + + if (options & eDynamicLibraryOpenOptionLocal) + mode |= RTLD_LOCAL; + else + mode |= RTLD_GLOBAL; + +#ifdef LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED + if (options & eDynamicLibraryOpenOptionLimitGetSymbol) + mode |= RTLD_FIRST; +#endif + + void * opaque = ::dlopen (path, mode); + + if (opaque) + { + error.Clear(); + return new DynamicLibraryInfo (file_spec, options, opaque); + } + else + { + error.SetErrorString(::dlerror()); + } + } + else + { + error.SetErrorString("failed to extract path"); + } + return NULL; +} + +Error +Host::DynamicLibraryClose (void *opaque) +{ + Error error; + if (opaque == NULL) + { + error.SetErrorString ("invalid dynamic library handle"); + } + else + { + DynamicLibraryInfo *dylib_info = (DynamicLibraryInfo *) opaque; + if (::dlclose (dylib_info->handle) != 0) + { + error.SetErrorString(::dlerror()); + } + + dylib_info->open_options = 0; + dylib_info->handle = 0; + delete dylib_info; + } + return error; +} + +void * +Host::DynamicLibraryGetSymbol (void *opaque, const char *symbol_name, Error &error) +{ + if (opaque == NULL) + { + error.SetErrorString ("invalid dynamic library handle"); + } + else + { + DynamicLibraryInfo *dylib_info = (DynamicLibraryInfo *) opaque; + + void *symbol_addr = ::dlsym (dylib_info->handle, symbol_name); + if (symbol_addr) + { +#ifndef LLDB_CONFIG_DLOPEN_RTLD_FIRST_SUPPORTED + // This host doesn't support limiting searches to this shared library + // so we need to verify that the match came from this shared library + // if it was requested in the Host::DynamicLibraryOpen() function. + if (dylib_info->open_options & eDynamicLibraryOpenOptionLimitGetSymbol) + { + FileSpec match_dylib_spec (Host::GetModuleFileSpecForHostAddress (symbol_addr)); + if (match_dylib_spec != dylib_info->file_spec) + { + char dylib_path[PATH_MAX]; + if (dylib_info->file_spec.GetPath (dylib_path, sizeof(dylib_path))) + error.SetErrorStringWithFormat ("symbol not found in \"%s\"", dylib_path); + else + error.SetErrorString ("symbol not found"); + return NULL; + } + } +#endif + error.Clear(); + return symbol_addr; + } + else + { + error.SetErrorString(::dlerror()); + } + } + return NULL; +} + +bool +Host::GetLLDBPath (PathType path_type, 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 linux this is assumed to be the "lldb" main executable. If LLDB on + // linux is actually in a shared library (liblldb.so) then this function will + // need to be modified to "do the right thing". + + switch (path_type) + { + case ePathTypeLLDBShlibDir: + { + static ConstString g_lldb_so_dir; + if (!g_lldb_so_dir) + { + FileSpec lldb_file_spec (Host::GetModuleFileSpecForHostAddress ((void *)Host::GetLLDBPath)); + g_lldb_so_dir = lldb_file_spec.GetDirectory(); + } + file_spec.GetDirectory() = g_lldb_so_dir; + return file_spec.GetDirectory(); + } + break; + + case ePathTypeSupportExecutableDir: + { + static ConstString g_lldb_support_exe_dir; + if (!g_lldb_support_exe_dir) + { + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + +#if defined (__APPLE__) + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); +#if !defined (__arm__) + ::strncpy (framework_pos, "/Resources", PATH_MAX - (framework_pos - raw_path)); +#endif + } +#endif + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_support_exe_dir.SetCString(resolved_path); + } + } + file_spec.GetDirectory() = g_lldb_support_exe_dir; + return file_spec.GetDirectory(); + } + break; + + case ePathTypeHeaderDir: + { + static ConstString g_lldb_headers_dir; + if (!g_lldb_headers_dir) + { +#if defined (__APPLE__) + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); + ::strncpy (framework_pos, "/Headers", PATH_MAX - (framework_pos - raw_path)); + } + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_headers_dir.SetCString(resolved_path); + } +#else + // TODO: Anyone know how we can determine this for linux? Other systems?? + g_lldb_headers_dir.SetCString ("/opt/local/include/lldb"); +#endif + } + file_spec.GetDirectory() = g_lldb_headers_dir; + return file_spec.GetDirectory(); + } + break; + +#ifndef LLDB_DISABLE_PYTHON + case ePathTypePythonDir: + { + static ConstString g_lldb_python_dir; + if (!g_lldb_python_dir) + { + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + +#if defined (__APPLE__) + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); + ::strncpy (framework_pos, "/Resources/Python", PATH_MAX - (framework_pos - raw_path)); + } +#else + llvm::SmallString<256> python_version_dir; + llvm::raw_svector_ostream os(python_version_dir); + os << "/python" << PY_MAJOR_VERSION << '.' << PY_MINOR_VERSION << "/site-packages"; + os.flush(); + + // We may get our string truncated. Should we protect + // this with an assert? + + ::strncat(raw_path, python_version_dir.c_str(), + sizeof(raw_path) - strlen(raw_path) - 1); + +#endif + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_python_dir.SetCString(resolved_path); + } + } + file_spec.GetDirectory() = g_lldb_python_dir; + return file_spec.GetDirectory(); + } + break; +#endif + + case ePathTypeLLDBSystemPlugins: // System plug-ins directory + { +#if defined (__APPLE__) || defined(__linux__) + static ConstString g_lldb_system_plugin_dir; + static bool g_lldb_system_plugin_dir_located = false; + if (!g_lldb_system_plugin_dir_located) + { + g_lldb_system_plugin_dir_located = true; +#if defined (__APPLE__) + FileSpec lldb_file_spec; + if (GetLLDBPath (ePathTypeLLDBShlibDir, lldb_file_spec)) + { + char raw_path[PATH_MAX]; + char resolved_path[PATH_MAX]; + lldb_file_spec.GetPath(raw_path, sizeof(raw_path)); + + char *framework_pos = ::strstr (raw_path, "LLDB.framework"); + if (framework_pos) + { + framework_pos += strlen("LLDB.framework"); + ::strncpy (framework_pos, "/Resources/PlugIns", PATH_MAX - (framework_pos - raw_path)); + FileSpec::Resolve (raw_path, resolved_path, sizeof(resolved_path)); + g_lldb_system_plugin_dir.SetCString(resolved_path); + } + return false; + } +#elif defined (__linux__) + FileSpec lldb_file_spec("/usr/lib/lldb", true); + if (lldb_file_spec.Exists()) + { + g_lldb_system_plugin_dir.SetCString(lldb_file_spec.GetPath().c_str()); + } +#endif // __APPLE__ || __linux__ + } + + if (g_lldb_system_plugin_dir) + { + file_spec.GetDirectory() = g_lldb_system_plugin_dir; + return true; + } +#else + // TODO: where would system LLDB plug-ins be located on other systems? + return false; +#endif + } + break; + + case ePathTypeLLDBUserPlugins: // User plug-ins directory + { +#if defined (__APPLE__) + static ConstString g_lldb_user_plugin_dir; + if (!g_lldb_user_plugin_dir) + { + char user_plugin_path[PATH_MAX]; + if (FileSpec::Resolve ("~/Library/Application Support/LLDB/PlugIns", + user_plugin_path, + sizeof(user_plugin_path))) + { + g_lldb_user_plugin_dir.SetCString(user_plugin_path); + } + } + file_spec.GetDirectory() = g_lldb_user_plugin_dir; + return file_spec.GetDirectory(); +#elif defined (__linux__) + static ConstString g_lldb_user_plugin_dir; + if (!g_lldb_user_plugin_dir) + { + // XDG Base Directory Specification + // http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + // If XDG_DATA_HOME exists, use that, otherwise use ~/.local/share/lldb. + FileSpec lldb_file_spec; + const char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (xdg_data_home && xdg_data_home[0]) + { + std::string user_plugin_dir (xdg_data_home); + user_plugin_dir += "/lldb"; + lldb_file_spec.SetFile (user_plugin_dir.c_str(), true); + } + else + { + const char *home_dir = getenv("HOME"); + if (home_dir && home_dir[0]) + { + std::string user_plugin_dir (home_dir); + user_plugin_dir += "/.local/share/lldb"; + lldb_file_spec.SetFile (user_plugin_dir.c_str(), true); + } + } + + if (lldb_file_spec.Exists()) + g_lldb_user_plugin_dir.SetCString(lldb_file_spec.GetPath().c_str()); + } + file_spec.GetDirectory() = g_lldb_user_plugin_dir; + return file_spec.GetDirectory(); +#endif + // TODO: where would user LLDB plug-ins be located on other systems? + return false; + } + } + + return false; +} + + +bool +Host::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; +} + +const char * +Host::GetUserName (uint32_t uid, std::string &user_name) +{ + 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) + { + if (user_info_ptr) + { + user_name.assign (user_info_ptr->pw_name); + return user_name.c_str(); + } + } + user_name.clear(); + return NULL; +} + +const char * +Host::GetGroupName (uint32_t gid, std::string &group_name) +{ + 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) + { + group_name.assign (group_info_ptr->gr_name); + return group_name.c_str(); + } + } + 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) + { + group_name.assign (group_info_ptr->gr_name); + return group_name.c_str(); + } + } + group_name.clear(); + return NULL; +} + +#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) // see macosx/Host.mm +bool +Host::GetOSBuildString (std::string &s) +{ + s.clear(); + return false; +} + +bool +Host::GetOSKernelDescription (std::string &s) +{ + s.clear(); + return false; +} +#endif + +uint32_t +Host::GetUserID () +{ + return getuid(); +} + +uint32_t +Host::GetGroupID () +{ + return getgid(); +} + +uint32_t +Host::GetEffectiveUserID () +{ + return geteuid(); +} + +uint32_t +Host::GetEffectiveGroupID () +{ + return getegid(); +} + +#if !defined (__APPLE__) && !defined(__linux__) +uint32_t +Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &process_infos) +{ + process_infos.Clear(); + return process_infos.GetSize(); +} +#endif // #if !defined (__APPLE__) && !defined(__linux__) + +#if !defined (__APPLE__) && !defined (__FreeBSD__) && !defined (__FreeBSD_kernel__) && !defined(__linux__) +bool +Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) +{ + process_info.Clear(); + return false; +} +#endif + +#if !defined(__linux__) +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + return false; +} +#endif + +lldb::TargetSP +Host::GetDummyTarget (lldb_private::Debugger &debugger) +{ + static TargetSP g_dummy_target_sp; + + // FIXME: Maybe the dummy target should be per-Debugger + if (!g_dummy_target_sp || !g_dummy_target_sp->IsValid()) + { + ArchSpec arch(Target::GetDefaultArchitecture()); + if (!arch.IsValid()) + arch = Host::GetArchitecture (); + Error err = debugger.GetTargetList().CreateTarget(debugger, + NULL, + arch.GetTriple().getTriple().c_str(), + false, + NULL, + g_dummy_target_sp); + } + + return g_dummy_target_sp; +} + +struct ShellInfo +{ + ShellInfo () : + process_reaped (false), + can_delete (false), + pid (LLDB_INVALID_PROCESS_ID), + signo(-1), + status(-1) + { + } + + lldb_private::Predicate process_reaped; + lldb_private::Predicate can_delete; + lldb::pid_t pid; + int signo; + int status; +}; + +static bool +MonitorShellCommand (void *callback_baton, + 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 +{ + ShellInfo *shell_info = (ShellInfo *)callback_baton; + 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(1, eBroadcastAlways); + // Now wait for a handshake back from that thread running Host::RunShellCommand + // so we know that we can delete shell_info_ptr + shell_info->can_delete.WaitForValueEqualTo(true); + // Sleep a bit to allow the shell_info->can_delete.SetValue() to complete... + usleep(1000); + // Now delete the shell info that was passed into this function + delete shell_info; + return true; +} + +Error +Host::RunShellCommand (const char *command, + const char *working_dir, + int *status_ptr, + int *signo_ptr, + std::string *command_output_ptr, + uint32_t timeout_sec, + const char *shell) +{ + Error error; + ProcessLaunchInfo launch_info; + if (shell && shell[0]) + { + // Run the command in a shell + launch_info.SetShell(shell); + launch_info.GetArguments().AppendArgument(command); + const bool localhost = true; + const bool will_debug = false; + const bool first_arg_is_full_shell_command = true; + launch_info.ConvertArgumentsForLaunchingInShell (error, + localhost, + will_debug, + first_arg_is_full_shell_command); + } + else + { + // No shell, just run it + Args args (command); + const bool first_arg_is_executable = true; + launch_info.SetArguments(args, first_arg_is_executable); + } + + if (working_dir) + launch_info.SetWorkingDirectory(working_dir); + char output_file_path_buffer[L_tmpnam]; + const char *output_file_path = NULL; + 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" + output_file_path = ::tmpnam(output_file_path_buffer); + launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); + launch_info.AppendOpenFileAction(STDOUT_FILENO, output_file_path, false, true); + launch_info.AppendDuplicateFileAction(STDOUT_FILENO, STDERR_FILENO); + } + else + { + launch_info.AppendSuppressFileAction (STDIN_FILENO, true, false); + launch_info.AppendSuppressFileAction (STDOUT_FILENO, false, true); + launch_info.AppendSuppressFileAction (STDERR_FILENO, false, true); + } + + // The process monitor callback will delete the 'shell_info_ptr' below... + std::unique_ptr shell_info_ap (new ShellInfo()); + + const bool monitor_signals = false; + launch_info.SetMonitorProcessCallback(MonitorShellCommand, shell_info_ap.get(), monitor_signals); + + error = LaunchProcess (launch_info); + const lldb::pid_t pid = launch_info.GetProcessID(); + if (pid != LLDB_INVALID_PROCESS_ID) + { + // The process successfully launched, so we can defer ownership of + // "shell_info" to the MonitorShellCommand callback function that will + // get called when the process dies. We release the unique pointer as it + // doesn't need to delete the ShellInfo anymore. + ShellInfo *shell_info = shell_info_ap.release(); + TimeValue timeout_time(TimeValue::Now()); + timeout_time.OffsetWithSeconds(timeout_sec); + bool timed_out = false; + shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out); + if (timed_out) + { + error.SetErrorString("timed out waiting for shell command to complete"); + + // Kill the process since it didn't complete withint the timeout specified + ::kill (pid, SIGKILL); + // Wait for the monitor callback to get the message + timeout_time = TimeValue::Now(); + timeout_time.OffsetWithSeconds(1); + timed_out = false; + shell_info->process_reaped.WaitForValueEqualTo(true, &timeout_time, &timed_out); + } + else + { + if (status_ptr) + *status_ptr = shell_info->status; + + if (signo_ptr) + *signo_ptr = shell_info->signo; + + if (command_output_ptr) + { + command_output_ptr->clear(); + FileSpec file_spec(output_file_path, File::eOpenOptionRead); + uint64_t file_size = file_spec.GetByteSize(); + 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 + { + command_output_ptr->resize(file_size); + file_spec.ReadFileContents(0, &((*command_output_ptr)[0]), command_output_ptr->size(), &error); + } + } + } + } + shell_info->can_delete.SetValue(true, eBroadcastAlways); + } + else + { + error.SetErrorString("failed to get process ID"); + } + + if (output_file_path) + ::unlink (output_file_path); + // Handshake with the monitor thread, or just let it know in advance that + // it can delete "shell_info" in case we timed out and were not able to kill + // the process... + return error; +} + + +uint32_t +Host::GetNumberCPUS () +{ + static uint32_t g_num_cores = UINT32_MAX; + if (g_num_cores == UINT32_MAX) + { +#if defined(__APPLE__) or defined (__linux__) or defined (__FreeBSD__) or defined (__FreeBSD_kernel__) + + g_num_cores = ::sysconf(_SC_NPROCESSORS_ONLN); + +#elif defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) + + // Header file for this might need to be included at the top of this file + SYSTEM_INFO system_info; + ::GetSystemInfo (&system_info); + g_num_cores = system_info.dwNumberOfProcessors; + +#else + + // Assume POSIX support if a host specific case has not been supplied above + g_num_cores = 0; + int num_cores = 0; + size_t num_cores_len = sizeof(num_cores); +#ifdef HW_AVAILCPU + int mib[] = { CTL_HW, HW_AVAILCPU }; +#else + int mib[] = { CTL_HW, HW_NCPU }; +#endif + + /* get the number of CPUs from the system */ + if (sysctl(mib, sizeof(mib)/sizeof(int), &num_cores, &num_cores_len, NULL, 0) == 0 && (num_cores > 0)) + { + g_num_cores = num_cores; + } + else + { + mib[1] = HW_NCPU; + num_cores_len = sizeof(num_cores); + if (sysctl(mib, sizeof(mib)/sizeof(int), &num_cores, &num_cores_len, NULL, 0) == 0 && (num_cores > 0)) + { + if (num_cores > 0) + g_num_cores = num_cores; + } + } +#endif + } + return g_num_cores; +} + + + +#if !defined (__APPLE__) +bool +Host::OpenFileInExternalEditor (const FileSpec &file_spec, uint32_t line_no) +{ + return false; +} + +void +Host::SetCrashDescriptionWithFormat (const char *format, ...) +{ +} + +void +Host::SetCrashDescription (const char *description) +{ +} + +lldb::pid_t +LaunchApplication (const FileSpec &app_file_spec) +{ + return LLDB_INVALID_PROCESS_ID; +} + +#endif diff --git a/source/Host/common/Mutex.cpp b/source/Host/common/Mutex.cpp new file mode 100644 index 00000000000..39cd8c6adb4 --- /dev/null +++ b/source/Host/common/Mutex.cpp @@ -0,0 +1,390 @@ +//===-- Mutex.cpp -----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Mutex.h" +#include "lldb/Host/Host.h" + +#include +#include +#include + +#if 0 +// This logging is way too verbose to enable even for a log channel. +// This logging can be enabled by changing the "#if 0", but should be +// reverted prior to checking in. +#include +#define DEBUG_LOG(fmt, ...) printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) +#endif + +// Enable extra mutex error checking +#ifdef LLDB_CONFIGURATION_DEBUG +#define ENABLE_MUTEX_ERROR_CHECKING 1 +#include +#endif + +#if ENABLE_MUTEX_ERROR_CHECKING +#include + +enum MutexAction +{ + eMutexActionInitialized, + eMutexActionDestroyed, + eMutexActionAssertInitialized +}; + +static bool +error_check_mutex (pthread_mutex_t *m, MutexAction action) +{ + typedef std::set mutex_set; + static pthread_mutex_t g_mutex_set_mutex = PTHREAD_MUTEX_INITIALIZER; + static mutex_set g_initialized_mutex_set; + static mutex_set g_destroyed_mutex_set; + + bool success = true; + int err; + // Manually call lock so we don't to any of this error checking + err = ::pthread_mutex_lock (&g_mutex_set_mutex); + assert(err == 0); + switch (action) + { + case eMutexActionInitialized: + // Make sure this isn't already in our initialized mutex set... + assert (g_initialized_mutex_set.find(m) == g_initialized_mutex_set.end()); + // Remove this from the destroyed set in case it was ever in there + g_destroyed_mutex_set.erase(m); + // Add the mutex to the initialized set + g_initialized_mutex_set.insert(m); + break; + + case eMutexActionDestroyed: + // Make sure this isn't already in our destroyed mutex set... + assert (g_destroyed_mutex_set.find(m) == g_destroyed_mutex_set.end()); + // Remove this from the initialized so we can put it into the destroyed set + g_initialized_mutex_set.erase(m); + // Add the mutex to the destroyed set + g_destroyed_mutex_set.insert(m); + break; + case eMutexActionAssertInitialized: + // This function will return true if "m" is in the initialized mutex set + success = g_initialized_mutex_set.find(m) != g_initialized_mutex_set.end(); + assert (success); + break; + } + // Manually call unlock so we don't to any of this error checking + err = ::pthread_mutex_unlock (&g_mutex_set_mutex); + assert(err == 0); + return success; +} + +#endif + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Default constructor. +// +// This will create a scoped mutex locking object that doesn't have +// a mutex to lock. One will need to be provided using the Reset() +// method. +//---------------------------------------------------------------------- +Mutex::Locker::Locker () : + m_mutex_ptr(NULL) +{ +} + +//---------------------------------------------------------------------- +// Constructor with a Mutex object. +// +// This will create a scoped mutex locking object that extracts the +// mutex owned by "m" and locks it. +//---------------------------------------------------------------------- +Mutex::Locker::Locker (Mutex& m) : + m_mutex_ptr(NULL) +{ + Lock (m); +} + +//---------------------------------------------------------------------- +// Constructor with a Mutex object pointer. +// +// This will create a scoped mutex locking object that extracts the +// mutex owned by "m" and locks it. +//---------------------------------------------------------------------- +Mutex::Locker::Locker (Mutex* m) : + m_mutex_ptr(NULL) +{ + if (m) + Lock (m); +} + +//---------------------------------------------------------------------- +// Destructor +// +// Unlocks any owned mutex object (if it is valid). +//---------------------------------------------------------------------- +Mutex::Locker::~Locker () +{ + Unlock(); +} + +//---------------------------------------------------------------------- +// Unlock the current mutex in this object (if this owns a valid +// mutex) and lock the new "mutex" object if it is non-NULL. +//---------------------------------------------------------------------- +void +Mutex::Locker::Lock (Mutex &mutex) +{ + // We already have this mutex locked or both are NULL... + if (m_mutex_ptr == &mutex) + return; + + Unlock (); + + m_mutex_ptr = &mutex; + m_mutex_ptr->Lock(); +} + +void +Mutex::Locker::Unlock () +{ + if (m_mutex_ptr) + { + m_mutex_ptr->Unlock (); + m_mutex_ptr = NULL; + } +} + +bool +Mutex::Locker::TryLock (Mutex &mutex, const char *failure_message) +{ + // We already have this mutex locked! + if (m_mutex_ptr == &mutex) + return true; + + Unlock (); + + if (mutex.TryLock(failure_message) == 0) + m_mutex_ptr = &mutex; + + return m_mutex_ptr != NULL; +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with no attributes. +//---------------------------------------------------------------------- +Mutex::Mutex () : + m_mutex() +{ + int err; + err = ::pthread_mutex_init (&m_mutex, NULL); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionInitialized); +#endif + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Default constructor. +// +// Creates a pthread mutex with "type" as the mutex type. +//---------------------------------------------------------------------- +Mutex::Mutex (Mutex::Type type) : + m_mutex() +{ + int err; + ::pthread_mutexattr_t attr; + err = ::pthread_mutexattr_init (&attr); + assert(err == 0); + switch (type) + { + case eMutexTypeNormal: +#if ENABLE_MUTEX_ERROR_CHECKING + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_ERRORCHECK); +#else + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_NORMAL); +#endif + break; + + case eMutexTypeRecursive: + err = ::pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE); + break; + } + assert(err == 0); + err = ::pthread_mutex_init (&m_mutex, &attr); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionInitialized); +#endif + assert(err == 0); + err = ::pthread_mutexattr_destroy (&attr); + assert(err == 0); +} + +//---------------------------------------------------------------------- +// Destructor. +// +// Destroys the mutex owned by this object. +//---------------------------------------------------------------------- +Mutex::~Mutex() +{ + int err = ::pthread_mutex_destroy (&m_mutex); + assert(err == 0); +#if ENABLE_MUTEX_ERROR_CHECKING + if (err == 0) + error_check_mutex (&m_mutex, eMutexActionDestroyed); + else + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_destroy() => err = %i (%s)", __PRETTY_FUNCTION__, err, strerror(err)); + assert(err == 0); + } + memset (&m_mutex, '\xba', sizeof(m_mutex)); +#endif +} + +//---------------------------------------------------------------------- +// Mutex get accessor. +//---------------------------------------------------------------------- +pthread_mutex_t * +Mutex::GetMutex() +{ + return &m_mutex; +} + +//---------------------------------------------------------------------- +// Locks the mutex owned by this object, if the mutex is already +// locked, the calling thread will block until the mutex becomes +// available. +// +// RETURNS +// The error code from the pthread_mutex_lock() function call. +//---------------------------------------------------------------------- +int +Mutex::Lock() +{ + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p)...\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex); + +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_lock (&m_mutex); + + +#if ENABLE_MUTEX_ERROR_CHECKING + if (err) + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_lock(%p) => err = %i (%s)", __PRETTY_FUNCTION__, &m_mutex, err, strerror(err)); + assert(err == 0); + } +#endif + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_lock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +//---------------------------------------------------------------------- +// Attempts to lock the mutex owned by this object without blocking. +// If the mutex is already locked, TryLock() will not block waiting +// for the mutex, but will return an error condition. +// +// RETURNS +// The error code from the pthread_mutex_trylock() function call. +//---------------------------------------------------------------------- +int +Mutex::TryLock(const char *failure_message) +{ +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_trylock (&m_mutex); + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_trylock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +//---------------------------------------------------------------------- +// If the current thread holds the lock on the owned mutex, then +// Unlock() will unlock the mutex. Calling Unlock() on this object +// that the calling thread does not hold will result in undefined +// behavior. +// +// RETURNS +// The error code from the pthread_mutex_unlock() function call. +//---------------------------------------------------------------------- +int +Mutex::Unlock() +{ +#if ENABLE_MUTEX_ERROR_CHECKING + error_check_mutex (&m_mutex, eMutexActionAssertInitialized); +#endif + + int err = ::pthread_mutex_unlock (&m_mutex); + +#if ENABLE_MUTEX_ERROR_CHECKING + if (err) + { + Host::SetCrashDescriptionWithFormat ("%s error: pthread_mutex_unlock(%p) => err = %i (%s)", __PRETTY_FUNCTION__, &m_mutex, err, strerror(err)); + assert(err == 0); + } +#endif + DEBUG_LOG ("[%4.4" PRIx64 "/%4.4" PRIx64 "] pthread_mutex_unlock (%p) => %i\n", Host::GetCurrentProcessID(), Host::GetCurrentThreadID(), &m_mutex, err); + return err; +} + +#ifdef LLDB_CONFIGURATION_DEBUG +int +TrackingMutex::Unlock () +{ + if (!m_failure_message.empty()) + Host::SetCrashDescriptionWithFormat ("Unlocking lock (on thread %p) that thread: %p failed to get: %s", + pthread_self(), + m_thread_that_tried, + m_failure_message.c_str()); + assert (m_failure_message.empty()); + return Mutex::Unlock(); +} + +int +LoggingMutex::Lock () +{ + printf("locking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::Lock(); + m_locked = true; + printf("%d\n",x); + return x; +} + +int +LoggingMutex::Unlock () +{ + printf("unlocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::Unlock(); + m_locked = false; + printf("%d\n",x); + return x; +} + +int +LoggingMutex::TryLock (const char *failure_message) +{ + printf("trylocking mutex %p by [%4.4" PRIx64 "/%4.4" PRIx64 "]...", this, Host::GetCurrentProcessID(), Host::GetCurrentThreadID()); + int x = Mutex::TryLock(failure_message); + if (x == 0) + m_locked = true; + printf("%d\n",x); + return x; +} + +#endif + + diff --git a/source/Host/common/SocketAddress.cpp b/source/Host/common/SocketAddress.cpp new file mode 100644 index 00000000000..a22dc7a01c1 --- /dev/null +++ b/source/Host/common/SocketAddress.cpp @@ -0,0 +1,260 @@ +//===-- SocketAddress.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/SocketAddress.h" +#include + +// C Includes +#include +#include + +// C++ Includes +// Other libraries and framework includes +// Project includes + +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 copy constructor +//---------------------------------------------------------------------- +SocketAddress::SocketAddress (const SocketAddress& rhs) : + m_socket_addr (rhs.m_socket_addr) +{ +} + +//---------------------------------------------------------------------- +// 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"); +} + +socklen_t +SocketAddress::GetLength () const +{ +#if defined(__APPLE__) + 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__) + m_socket_addr.sa.sa_len = GetFamilyLength (family); +#endif +} + +in_port_t +SocketAddress::GetPort () const +{ + switch (GetFamily()) + { + case AF_INET: return m_socket_addr.sa_ipv4.sin_port; + case AF_INET6: return m_socket_addr.sa_ipv6.sin6_port; + } + return 0; +} + +bool +SocketAddress::SetPort (in_port_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&& + 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::SetAddress (const struct addrinfo *hints_ptr, + const char *host, + const char *service, + struct addrinfo *addr_info_ptr) +{ + struct addrinfo *service_info_list = NULL; + int err = ::getaddrinfo (host, service, hints_ptr, &service_info_list); + if (err == 0 && service_info_list) + { + if (addr_info_ptr) + *addr_info_ptr = *service_info_list; + *this = service_info_list; + } + else + Clear(); + + :: freeaddrinfo (service_info_list); + + const bool is_valid = IsValid(); + if (!is_valid) + { + if (addr_info_ptr) + ::memset (addr_info_ptr, 0, sizeof(struct addrinfo)); + } + return is_valid; +} + + +bool +SocketAddress::SetToLocalhost (sa_family_t family, in_port_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; +} diff --git a/source/Host/common/Symbols.cpp b/source/Host/common/Symbols.cpp new file mode 100644 index 00000000000..7189269677d --- /dev/null +++ b/source/Host/common/Symbols.cpp @@ -0,0 +1,163 @@ +//===-- Symbols.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Symbols.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +#if defined (__linux__) || defined (__FreeBSD__) + +FileSpec +Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) +{ + // FIXME + return FileSpec(); +} + +FileSpec +Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) +{ + const char *symbol_filename = module_spec.GetSymbolFileSpec().GetFilename().AsCString(); + if (!symbol_filename || !symbol_filename[0]) + return FileSpec(); + + FileSpecList debug_file_search_paths (Target::GetDefaultDebugFileSearchPaths()); + + // Add module directory. + const ConstString &file_dir = module_spec.GetFileSpec().GetDirectory(); + debug_file_search_paths.AppendIfUnique (FileSpec(file_dir.AsCString("."), true)); + + // Add current working directory. + debug_file_search_paths.AppendIfUnique (FileSpec(".", true)); + + // Add /usr/lib/debug directory. + debug_file_search_paths.AppendIfUnique (FileSpec("/usr/lib/debug", true)); + + 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(""); + uuid_str.insert (2, 1, '/'); + uuid_str = uuid_str + ".debug"; + } + + // Get full path to our module. Needed to check debug files like this: + // /usr/lib/debug/usr/lib/libboost_date_time.so.1.46.1 + std::string module_filename = module_spec.GetFileSpec().GetPath(); + + 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); + dirspec.ResolvePath(); + if (!dirspec.Exists() || !dirspec.IsDirectory()) + continue; + + std::vector 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); + files.push_back (dirname + module_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.c_str(), true); + + if (file_spec == module_spec.GetFileSpec()) + continue; + + if (file_spec.Exists()) + { + 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)) + { + if (mspec.GetUUID() == module_uuid) + return file_spec; + } + } + } + } + } + + return FileSpec(); +} + +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; +} + +#elif !defined (__APPLE__) + +FileSpec +Symbols::LocateExecutableObjectFile (const ModuleSpec &module_spec) +{ + // FIXME + return FileSpec(); +} + +FileSpec +Symbols::LocateExecutableSymbolFile (const ModuleSpec &module_spec) +{ + // FIXME + return FileSpec(); +} + +FileSpec +Symbols::FindSymbolFileInBundle (const FileSpec& symfile_bundle, + const lldb_private::UUID *uuid, + const ArchSpec *arch) +{ + 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/source/Host/common/Terminal.cpp b/source/Host/common/Terminal.cpp new file mode 100644 index 00000000000..89d21cf0bf6 --- /dev/null +++ b/source/Host/common/Terminal.cpp @@ -0,0 +1,307 @@ +//===-- Terminal.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/Terminal.h" +#include "lldb/Host/Config.h" + +#include +#include +#include + +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED +#include +#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), + m_termios_ap(), + m_process_group(-1) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TerminalState::~TerminalState() +{ +} + +void +TerminalState::Clear () +{ + m_tty.Clear(); + m_tflags = -1; + m_termios_ap.reset(); + 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()) + { + m_tflags = ::fcntl (fd, F_GETFL, 0); +#ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (m_termios_ap.get() == NULL) + m_termios_ap.reset (new struct termios); + int err = ::tcgetattr (fd, m_termios_ap.get()); + if (err != 0) + m_termios_ap.reset(); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + if (save_process_group) + m_process_group = ::tcgetpgrp (0); + else + m_process_group = -1; + } + else + { + m_tty.Clear(); + m_tflags = -1; + m_termios_ap.reset(); + 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 +{ + 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_ap.get()); +#endif // #ifdef LLDB_CONFIG_TERMIOS_SUPPORTED + + if (ProcessGroupIsValid()) + { + // Save the original signal handler. + void (*saved_sigttou_callback) (int) = NULL; + 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; + } + 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 +{ + return m_termios_ap.get() != 0; +} + +//---------------------------------------------------------------------- +// Returns true if m_process_group is valid +//---------------------------------------------------------------------- +bool +TerminalState::ProcessGroupIsValid() const +{ + return 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 sizeof(m_ttystates)/sizeof(TerminalState); +} + +//------------------------------------------------------------------ +// 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/source/Host/common/TimeValue.cpp b/source/Host/common/TimeValue.cpp new file mode 100644 index 00000000000..303ac94058b --- /dev/null +++ b/source/Host/common/TimeValue.cpp @@ -0,0 +1,210 @@ +//===-- TimeValue.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Host/TimeValue.h" + +// C Includes +#include +#include +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" + + +using namespace lldb_private; + +//---------------------------------------------------------------------- +// TimeValue constructor +//---------------------------------------------------------------------- +TimeValue::TimeValue() : + m_nano_seconds (0) +{ +} + +//---------------------------------------------------------------------- +// TimeValue copy constructor +//---------------------------------------------------------------------- +TimeValue::TimeValue(const TimeValue& rhs) : + m_nano_seconds (rhs.m_nano_seconds) +{ +} + +TimeValue::TimeValue(const struct timespec& ts) : + m_nano_seconds ((uint64_t) ts.tv_sec * NanoSecPerSec + ts.tv_nsec) +{ +} + +TimeValue::TimeValue(const struct timeval& tv) : + m_nano_seconds ((uint64_t) tv.tv_sec * NanoSecPerSec + (uint64_t) tv.tv_usec * NanoSecPerMicroSec) +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +TimeValue::~TimeValue() +{ +} + + +uint64_t +TimeValue::GetAsNanoSecondsSinceJan1_1970() const +{ + return m_nano_seconds; +} + +uint64_t +TimeValue::GetAsMicroSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NanoSecPerMicroSec; +} + +uint64_t +TimeValue::GetAsSecondsSinceJan1_1970() const +{ + return m_nano_seconds / NanoSecPerSec; +} + + + +struct timespec +TimeValue::GetAsTimeSpec () const +{ + struct timespec ts; + ts.tv_sec = m_nano_seconds / NanoSecPerSec; + ts.tv_nsec = m_nano_seconds % NanoSecPerSec; + return ts; +} + +struct timeval +TimeValue::GetAsTimeVal () const +{ + struct timeval tv; + tv.tv_sec = m_nano_seconds / NanoSecPerSec; + tv.tv_usec = (m_nano_seconds % NanoSecPerSec) / NanoSecPerMicroSec; + return tv; +} + +void +TimeValue::Clear () +{ + m_nano_seconds = 0; +} + +bool +TimeValue::IsValid () const +{ + return m_nano_seconds != 0; +} + +void +TimeValue::OffsetWithSeconds (uint64_t sec) +{ + m_nano_seconds += sec * NanoSecPerSec; +} + +void +TimeValue::OffsetWithMicroSeconds (uint64_t usec) +{ + m_nano_seconds += usec * NanoSecPerMicroSec; +} + +void +TimeValue::OffsetWithNanoSeconds (uint64_t nsec) +{ + m_nano_seconds += nsec; +} + +TimeValue +TimeValue::Now() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + TimeValue now(tv); + return now; +} + +//---------------------------------------------------------------------- +// TimeValue assignment operator +//---------------------------------------------------------------------- +const TimeValue& +TimeValue::operator=(const TimeValue& rhs) +{ + m_nano_seconds = rhs.m_nano_seconds; + return *this; +} + +void +TimeValue::Dump (Stream *s, uint32_t width) const +{ + if (s == NULL) + return; + + char time_buf[32]; + time_t time = GetAsSecondsSinceJan1_1970(); + char *time_cstr = ::ctime_r(&time, time_buf); + if (time_cstr) + { + char *newline = ::strpbrk(time_cstr, "\n\r"); + if (newline) + *newline = '\0'; + if (width > 0) + s->Printf("%-*s", width, time_cstr); + else + s->PutCString(time_cstr); + } + else if (width > 0) + s->Printf("%-*s", width, ""); +} + +bool +lldb_private::operator == (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() == rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator != (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() != rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator < (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() < rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator <= (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() <= rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator > (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() > rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +bool +lldb_private::operator >= (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() >= rhs.GetAsNanoSecondsSinceJan1_1970(); +} + +uint64_t +lldb_private::operator - (const TimeValue &lhs, const TimeValue &rhs) +{ + return lhs.GetAsNanoSecondsSinceJan1_1970() - rhs.GetAsNanoSecondsSinceJan1_1970(); +} + + diff --git a/source/Host/freebsd/Host.cpp b/source/Host/freebsd/Host.cpp new file mode 100644 index 00000000000..405e7eacf5a --- /dev/null +++ b/source/Host/freebsd/Host.cpp @@ -0,0 +1,337 @@ +//===-- source/Host/freebsd/Host.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Host/Endian.h" +#include "lldb/Host/Host.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Process.h" + +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "llvm/Support/Host.h" + + +extern "C" { + extern char **environ; +} + +using namespace lldb; +using namespace lldb_private; + + +class FreeBSDThread +{ +public: + FreeBSDThread(const char *thread_name) + { + Host::SetThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name); + } + static void PThreadDestructor (void *v) + { + delete (FreeBSDThread*)v; + } +}; + +static pthread_once_t g_thread_create_once = PTHREAD_ONCE_INIT; +static pthread_key_t g_thread_create_key = 0; + +static void +InitThreadCreated() +{ + ::pthread_key_create (&g_thread_create_key, FreeBSDThread::PThreadDestructor); +} + +void +Host::ThreadCreated (const char *thread_name) +{ + ::pthread_once (&g_thread_create_once, InitThreadCreated); + if (g_thread_create_key) + { + ::pthread_setspecific (g_thread_create_key, new FreeBSDThread(thread_name)); + } + + Host::SetShortThreadName (LLDB_INVALID_PROCESS_ID, LLDB_INVALID_THREAD_ID, thread_name, 16); +} + +std::string +Host::GetThreadName (lldb::pid_t pid, lldb::tid_t tid) +{ + std::string thread_name; + return thread_name; +} + +void +Host::Backtrace (Stream &strm, uint32_t max_frames) +{ + char backtrace_path[] = "/tmp/lldb-backtrace-tmp-XXXXXX"; + int backtrace_fd = ::mkstemp (backtrace_path); + if (backtrace_fd != -1) + { + std::vector frame_buffer (max_frames, NULL); + int count = ::backtrace (&frame_buffer[0], frame_buffer.size()); + ::backtrace_symbols_fd (&frame_buffer[0], count, backtrace_fd); + + const off_t buffer_size = ::lseek(backtrace_fd, 0, SEEK_CUR); + + if (::lseek(backtrace_fd, 0, SEEK_SET) == 0) + { + char *buffer = (char *)::malloc (buffer_size); + if (buffer) + { + ssize_t bytes_read = ::read (backtrace_fd, buffer, buffer_size); + if (bytes_read > 0) + strm.Write(buffer, bytes_read); + ::free (buffer); + } + } + ::close (backtrace_fd); + ::unlink (backtrace_path); + } +} + +size_t +Host::GetEnvironment (StringList &env) +{ + char *v; + char **var = environ; + for (; var != NULL && *var != NULL; ++var) { + v = strchr(*var, (int)'-'); + if (v == NULL) + continue; + env.AppendString(v); + } + return env.GetSize(); +} + +bool +Host::GetOSVersion(uint32_t &major, + uint32_t &minor, + uint32_t &update) +{ + struct utsname un; + int status; + + if (uname(&un) < 0) + return false; + + status = sscanf(un.release, "%u.%u", &major, &minor); + return status == 2; +} + +Error +Host::LaunchProcess (ProcessLaunchInfo &launch_info) +{ + Error error; + assert(!"Not implemented yet!!!"); + return error; +} + +bool +Host::GetOSBuildString (std::string &s) +{ + int mib[2] = { CTL_KERN, KERN_OSREV }; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign (cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +bool +Host::GetOSKernelDescription (std::string &s) +{ + int mib[2] = { CTL_KERN, KERN_VERSION }; + char cstr[PATH_MAX]; + size_t cstr_len = sizeof(cstr); + if (::sysctl (mib, 2, cstr, &cstr_len, NULL, 0) == 0) + { + s.assign (cstr, cstr_len); + return true; + } + s.clear(); + return false; +} + +static bool +GetFreeBSDProcessArgs (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, lldb::endian::InlHostByteOrder(), sizeof(void *)); + lldb::offset_t offset = 0; + const char *cstr; + + cstr = data.GetCStr (&offset); + if (cstr) + { + process_info.GetExecutableFile().SetFile(cstr, false); + + 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(cstr); + else + return true; + } + } + } + } + return false; +} + +static bool +GetFreeBSDProcessCPUType (ProcessInstanceInfo &process_info) +{ + if (process_info.ProcessIDIsValid()) { + process_info.GetArchitecture() = Host::GetArchitecture (Host::eSystemDefaultArchitecture); + return true; + } + process_info.GetArchitecture().Clear(); + return false; +} + +static bool +GetFreeBSDProcessUserAndGroup(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.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; + } + } + } + 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; +} + +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; +} + +lldb::DataBufferSP +Host::GetAuxvData(lldb_private::Process *process) +{ + int mib[2] = { CTL_KERN, KERN_PS_STRINGS }; + void *ps_strings_addr, *auxv_addr; + size_t ps_strings_size = sizeof(void *); + Elf_Auxinfo aux_info[AT_COUNT]; + struct ps_strings ps_strings; + struct ptrace_io_desc pid; + DataBufferSP buf_sp; + std::unique_ptr buf_ap(new DataBufferHeap(1024, 0)); + + if (::sysctl(mib, 2, &ps_strings_addr, &ps_strings_size, NULL, 0) == 0) { + pid.piod_op = PIOD_READ_D; + pid.piod_addr = &ps_strings; + pid.piod_offs = ps_strings_addr; + pid.piod_len = sizeof(ps_strings); + if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) { + perror("failed to fetch ps_strings"); + buf_ap.release(); + goto done; + } + + auxv_addr = ps_strings.ps_envstr + ps_strings.ps_nenvstr + 1; + + pid.piod_addr = aux_info; + pid.piod_offs = auxv_addr; + pid.piod_len = sizeof(aux_info); + if (::ptrace(PT_IO, process->GetID(), (caddr_t)&pid, 0)) { + perror("failed to fetch aux_info"); + buf_ap.release(); + goto done; + } + memcpy(buf_ap->GetBytes(), aux_info, pid.piod_len); + buf_sp.reset(buf_ap.release()); + } else { + perror("sysctl failed on ps_strings"); + } + + done: + return buf_sp; +} diff --git a/source/Interpreter/Args.cpp b/source/Interpreter/Args.cpp new file mode 100644 index 00000000000..e6d20435803 --- /dev/null +++ b/source/Interpreter/Args.cpp @@ -0,0 +1,1789 @@ +//===-- Args.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +// C Includes +#include +#include +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.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" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// Args constructor +//---------------------------------------------------------------------- +Args::Args (const char *command) : + m_args(), + m_argv(), + m_args_quote_char() +{ + if (command) + SetCommandString (command); +} + + +Args::Args (const char *command, size_t len) : + m_args(), + m_argv(), + m_args_quote_char() +{ + if (command && len) + SetCommandString (command, len); +} + +//---------------------------------------------------------------------- +// We have to be very careful on the copy constructor of this class +// to make sure we copy all of the string values, but we can't copy the +// rhs.m_argv into m_argv since it will point to the "const char *" c +// strings in rhs.m_args. We need to copy the string list and update our +// own m_argv appropriately. +//---------------------------------------------------------------------- +Args::Args (const Args &rhs) : + m_args (rhs.m_args), + m_argv (), + m_args_quote_char(rhs.m_args_quote_char) +{ + UpdateArgvFromArgs(); +} + +//---------------------------------------------------------------------- +// We have to be very careful on the copy constructor of this class +// to make sure we copy all of the string values, but we can't copy the +// rhs.m_argv into m_argv since it will point to the "const char *" c +// strings in rhs.m_args. We need to copy the string list and update our +// own m_argv appropriately. +//---------------------------------------------------------------------- +const Args & +Args::operator= (const Args &rhs) +{ + // Make sure we aren't assigning to self + if (this != &rhs) + { + m_args = rhs.m_args; + m_args_quote_char = rhs.m_args_quote_char; + UpdateArgvFromArgs(); + } + return *this; +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Args::~Args () +{ +} + +void +Args::Dump (Stream *s) +{ + const size_t argc = m_argv.size(); + for (size_t i=0; iIndent(); + const char *arg_cstr = m_argv[i]; + if (arg_cstr) + s->Printf("argv[%zi]=\"%s\"\n", i, arg_cstr); + else + s->Printf("argv[%zi]=NULL\n", i); + } + s->EOL(); +} + +bool +Args::GetCommandString (std::string &command) const +{ + command.clear(); + const size_t argc = GetArgumentCount(); + for (size_t i=0; i 0) + command += ' '; + command += m_argv[i]; + } + return argc > 0; +} + +bool +Args::GetQuotedCommandString (std::string &command) const +{ + command.clear (); + const size_t argc = GetArgumentCount(); + for (size_t i = 0; i < argc; ++i) + { + if (i > 0) + command.append (1, ' '); + char quote_char = GetArgumentQuoteCharAtIndex(i); + if (quote_char) + { + command.append (1, quote_char); + command.append (m_argv[i]); + command.append (1, quote_char); + } + else + command.append (m_argv[i]); + } + return argc > 0; +} + +void +Args::SetCommandString (const char *command, size_t len) +{ + // Use std::string to make sure we get a NULL terminated string we can use + // as "command" could point to a string within a large string.... + std::string null_terminated_command(command, len); + SetCommandString(null_terminated_command.c_str()); +} + +void +Args::SetCommandString (const char *command) +{ + m_args.clear(); + m_argv.clear(); + m_args_quote_char.clear(); + + if (command && command[0]) + { + static const char *k_space_separators = " \t"; + static const char *k_space_separators_with_slash_and_quotes = " \t \\'\""; + const char *arg_end = NULL; + const char *arg_pos; + for (arg_pos = command; + arg_pos && arg_pos[0]; + arg_pos = arg_end) + { + // Skip any leading space separators + const char *arg_start = ::strspn (arg_pos, k_space_separators) + arg_pos; + + // If there were only space separators to the end of the line, then + // we're done. + if (*arg_start == '\0') + break; + + // Arguments 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. So we need to keep track of the + // current start of each argument piece in "arg_piece_start" + const char *arg_piece_start = arg_start; + arg_pos = arg_piece_start; + + 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'; + char quote_char = '\0'; + bool arg_complete = false; + + do + { + arg_end = ::strcspn (arg_pos, k_space_separators_with_slash_and_quotes) + arg_pos; + + switch (arg_end[0]) + { + default: + assert (!"Unhandled case statement, we must handle this..."); + break; + + case '\0': + // End of C string + if (arg_piece_start && arg_piece_start[0]) + arg.append (arg_piece_start); + arg_complete = true; + break; + + case '\\': + // Backslash character + switch (arg_end[1]) + { + case '\0': + arg.append (arg_piece_start); + ++arg_end; + arg_complete = true; + break; + + default: + if (quote_char == '\0') + { + arg.append (arg_piece_start, arg_end - arg_piece_start); + if (arg_end[1] != '\0') + { + arg.append (arg_end + 1, 1); + arg_pos = arg_end + 2; + arg_piece_start = arg_pos; + } + } + else + arg_pos = arg_end + 2; + break; + } + break; + + case '"': + case '\'': + case '`': + // Quote characters + if (quote_char) + { + // We found a quote character while inside a quoted + // character argument. If it matches our current quote + // character, this ends the effect of the quotes. If it + // doesn't we ignore it. + if (quote_char == arg_end[0]) + { + arg.append (arg_piece_start, arg_end - arg_piece_start); + // Clear the quote character and let parsing + // continue (we need to watch for things like: + // "Hello ""World" + // "Hello "World + // "Hello "'World' + // All of which will result in a single argument "Hello World" + quote_char = '\0'; // Note that we are no longer inside quotes + arg_pos = arg_end + 1; // Skip the quote character + arg_piece_start = arg_pos; // Note we are starting from later in the string + } + else + { + // different quote, skip it and keep going + arg_pos = arg_end + 1; + } + } + else + { + // We found the start of a quote scope. + // Make sure there isn't a string that precedes + // the start of a quote scope like: + // Hello" World" + // If so, then add the "Hello" to the arg + if (arg_end > arg_piece_start) + arg.append (arg_piece_start, arg_end - arg_piece_start); + + // Enter into a quote scope + quote_char = arg_end[0]; + + if (first_quote_char == '\0') + first_quote_char = quote_char; + + arg_pos = arg_end; + ++arg_pos; // Skip the quote character + arg_piece_start = arg_pos; // Note we are starting from later in the string + + // Skip till the next quote character + const char *end_quote = ::strchr (arg_piece_start, quote_char); + while (end_quote && end_quote[-1] == '\\') + { + // Don't skip the quote character if it is + // preceded by a '\' character + end_quote = ::strchr (end_quote + 1, quote_char); + } + + if (end_quote) + { + if (end_quote > arg_piece_start) + arg.append (arg_piece_start, end_quote - arg_piece_start); + + // If the next character is a space or the end of + // string, this argument is complete... + if (end_quote[1] == ' ' || end_quote[1] == '\t' || end_quote[1] == '\0') + { + arg_complete = true; + arg_end = end_quote + 1; + } + else + { + arg_pos = end_quote + 1; + arg_piece_start = arg_pos; + } + quote_char = '\0'; + } + else + { + // Consume the rest of the string as there was no terminating quote + arg.append(arg_piece_start); + arg_end = arg_piece_start + strlen(arg_piece_start); + arg_complete = true; + } + } + break; + + case ' ': + case '\t': + if (quote_char) + { + // We are currently processing a quoted character and found + // a space character, skip any spaces and keep trying to find + // the end of the argument. + arg_pos = ::strspn (arg_end, k_space_separators) + arg_end; + } + else + { + // We are not inside any quotes, we just found a space after an + // argument + if (arg_end > arg_piece_start) + arg.append (arg_piece_start, arg_end - arg_piece_start); + arg_complete = true; + } + break; + } + } while (!arg_complete); + + m_args.push_back(arg); + m_args_quote_char.push_back (first_quote_char); + } + UpdateArgvFromArgs(); + } +} + +void +Args::UpdateArgsAfterOptionParsing() +{ + // Now m_argv might be out of date with m_args, so we need to fix that + arg_cstr_collection::const_iterator argv_pos, argv_end = m_argv.end(); + arg_sstr_collection::iterator args_pos; + arg_quote_char_collection::iterator quotes_pos; + + for (argv_pos = m_argv.begin(), args_pos = m_args.begin(), quotes_pos = m_args_quote_char.begin(); + argv_pos != argv_end && args_pos != m_args.end(); + ++argv_pos) + { + const char *argv_cstr = *argv_pos; + if (argv_cstr == NULL) + break; + + while (args_pos != m_args.end()) + { + const char *args_cstr = args_pos->c_str(); + if (args_cstr == argv_cstr) + { + // We found the argument that matches the C string in the + // vector, so we can now look for the next one + ++args_pos; + ++quotes_pos; + break; + } + else + { + quotes_pos = m_args_quote_char.erase (quotes_pos); + args_pos = m_args.erase (args_pos); + } + } + } + + if (args_pos != m_args.end()) + m_args.erase (args_pos, m_args.end()); + + if (quotes_pos != m_args_quote_char.end()) + m_args_quote_char.erase (quotes_pos, m_args_quote_char.end()); +} + +void +Args::UpdateArgvFromArgs() +{ + m_argv.clear(); + arg_sstr_collection::const_iterator pos, end = m_args.end(); + for (pos = m_args.begin(); pos != end; ++pos) + m_argv.push_back(pos->c_str()); + m_argv.push_back(NULL); + // Make sure we have enough arg quote chars in the array + if (m_args_quote_char.size() < m_args.size()) + m_args_quote_char.resize (m_argv.size()); +} + +size_t +Args::GetArgumentCount() const +{ + if (m_argv.empty()) + return 0; + return m_argv.size() - 1; +} + +const char * +Args::GetArgumentAtIndex (size_t idx) const +{ + if (idx < m_argv.size()) + return m_argv[idx]; + return NULL; +} + +char +Args::GetArgumentQuoteCharAtIndex (size_t idx) const +{ + if (idx < m_args_quote_char.size()) + return m_args_quote_char[idx]; + return '\0'; +} + +char ** +Args::GetArgumentVector() +{ + if (!m_argv.empty()) + return (char **)&m_argv[0]; + return NULL; +} + +const char ** +Args::GetConstArgumentVector() const +{ + if (!m_argv.empty()) + return (const char **)&m_argv[0]; + return NULL; +} + +void +Args::Shift () +{ + // Don't pop the last NULL terminator from the argv array + if (m_argv.size() > 1) + { + m_argv.erase(m_argv.begin()); + m_args.pop_front(); + if (!m_args_quote_char.empty()) + m_args_quote_char.erase(m_args_quote_char.begin()); + } +} + +const char * +Args::Unshift (const char *arg_cstr, char quote_char) +{ + m_args.push_front(arg_cstr); + m_argv.insert(m_argv.begin(), m_args.front().c_str()); + m_args_quote_char.insert(m_args_quote_char.begin(), quote_char); + return GetArgumentAtIndex (0); +} + +void +Args::AppendArguments (const Args &rhs) +{ + const size_t rhs_argc = rhs.GetArgumentCount(); + for (size_t i=0; i 0 && pos != end; ++pos) + --i; + + pos = m_args.insert(pos, arg_cstr); + + if (idx >= m_args_quote_char.size()) + { + m_args_quote_char.resize(idx + 1); + m_args_quote_char[idx] = quote_char; + } + else + m_args_quote_char.insert(m_args_quote_char.begin() + idx, quote_char); + + UpdateArgvFromArgs(); + return GetArgumentAtIndex(idx); +} + +const char * +Args::ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + pos->assign(arg_cstr); + assert(idx < m_argv.size() - 1); + m_argv[idx] = pos->c_str(); + if (idx >= m_args_quote_char.size()) + m_args_quote_char.resize(idx + 1); + m_args_quote_char[idx] = quote_char; + return GetArgumentAtIndex(idx); + } + return NULL; +} + +void +Args::DeleteArgumentAtIndex (size_t idx) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + m_args.erase (pos); + assert(idx < m_argv.size() - 1); + m_argv.erase(m_argv.begin() + idx); + if (idx < m_args_quote_char.size()) + m_args_quote_char.erase(m_args_quote_char.begin() + idx); + } +} + +void +Args::SetArguments (size_t argc, const char **argv) +{ + // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is + // no need to clear it here. + m_args.clear(); + m_args_quote_char.clear(); + + // First copy each string + for (size_t i=0; iOptionSeen (val); + + // Lookup the long option index + if (long_options_index == -1) + { + for (int i=0; + long_options[i].name || long_options[i].has_arg || 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) + { + error = options.SetOptionValue(long_options_index, + long_options[long_options_index].has_arg == no_argument ? NULL : optarg); + } + else + { + error.SetErrorStringWithFormat("invalid option with value '%i'", val); + } + if (error.Fail()) + break; + } + + // Update our ARGV now that get options has consumed all the options + m_argv.erase(m_argv.begin(), m_argv.begin() + optind); + UpdateArgsAfterOptionParsing (); + return error; +} + +void +Args::Clear () +{ + m_args.clear (); + m_argv.clear (); + m_args_quote_char.clear(); +} + +int32_t +Args::StringToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + 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 +Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + 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 +Args::StringToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + 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 +Args::StringToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + 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; +} + +lldb::addr_t +Args::StringToAddress (const ExecutionContext *exe_ctx, const char *s, lldb::addr_t fail_value, Error *error_ptr) +{ + bool error_set = false; + if (s && s[0]) + { + char *end = NULL; + lldb::addr_t addr = ::strtoull (s, &end, 0); + if (*end == '\0') + { + if (error_ptr) + error_ptr->Clear(); + return addr; // All characters were used, return the result + } + // Try base 16 with no prefix... + addr = ::strtoull (s, &end, 16); + if (*end == '\0') + { + if (error_ptr) + error_ptr->Clear(); + return addr; // All characters were used, return the result + } + + if (exe_ctx) + { + Target *target = exe_ctx->GetTargetPtr(); + if (target) + { + lldb::ValueObjectSP valobj_sp; + EvaluateExpressionOptions options; + options.SetCoerceToId(false); + options.SetUnwindOnError(true); + options.SetKeepInMemory(false); + options.SetRunOthers(true); + + ExecutionResults expr_result = target->EvaluateExpression(s, + exe_ctx->GetFramePtr(), + valobj_sp, + options); + + bool success = false; + if (expr_result == eExecutionCompleted) + { + // Get the address to watch. + 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, valobj_sp->GetTypeName().GetCString()); + } + } + + } + else + { + // Since the compiler can't handle things like "main + 12" we should + // try to do this for now. The compliler 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(s, ®ex_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)) + { + offset = Args::StringToUInt64(str.c_str(), 0, 0, &success); + + if (success) + { + Error error; + addr = StringToAddress (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); + } + } + } + } + } + if (error_ptr) + { + if (!error_set) + error_ptr->SetErrorStringWithFormat("invalid address expression \"%s\"", s); + } + return fail_value; +} + +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 NULL; + return s.c_str(); +} + +bool +Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if (::strcasecmp (s, "false") == 0 || + ::strcasecmp (s, "off") == 0 || + ::strcasecmp (s, "no") == 0 || + ::strcmp (s, "0") == 0) + { + if (success_ptr) + *success_ptr = true; + return false; + } + else + if (::strcasecmp (s, "true") == 0 || + ::strcasecmp (s, "on") == 0 || + ::strcasecmp (s, "yes") == 0 || + ::strcmp (s, "1") == 0) + { + if (success_ptr) *success_ptr = true; + return true; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +const char * +Args::StringToVersion (const char *s, uint32_t &major, uint32_t &minor, uint32_t &update) +{ + major = UINT32_MAX; + minor = UINT32_MAX; + update = UINT32_MAX; + + if (s && s[0]) + { + char *pos = NULL; + unsigned long uval32 = ::strtoul (s, &pos, 0); + if (pos == s) + return s; + major = uval32; + if (*pos == '\0') + { + return pos; // Decoded major and got end of string + } + else if (*pos == '.') + { + const char *minor_cstr = pos + 1; + uval32 = ::strtoul (minor_cstr, &pos, 0); + if (pos == minor_cstr) + return pos; // Didn't get any digits for the minor version... + minor = uval32; + if (*pos == '.') + { + const char *update_cstr = pos + 1; + uval32 = ::strtoul (update_cstr, &pos, 0); + if (pos == update_cstr) + return pos; + update = uval32; + } + return pos; + } + } + return 0; +} + +const char * +Args::GetShellSafeArgument (const char *unsafe_arg, std::string &safe_arg) +{ + 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(" '\"", prev_pos); + if (pos != std::string::npos) + { + safe_arg.insert (pos, 1, '\\'); + prev_pos = pos + 2; + } + else + break; + } + return safe_arg.c_str(); +} + + +int64_t +Args::StringToOptionEnum (const char *s, OptionEnumValueElement *enum_values, int32_t fail_value, Error &error) +{ + if (enum_values) + { + if (s && s[0]) + { + for (int i = 0; enum_values[i].string_value != NULL ; i++) + { + if (strstr(enum_values[i].string_value, s) == enum_values[i].string_value) + { + error.Clear(); + return enum_values[i].value; + } + } + } + + StreamString strm; + strm.PutCString ("invalid enumeration value, valid values are: "); + for (int i = 0; enum_values[i].string_value != NULL; i++) + { + strm.Printf ("%s\"%s\"", + i > 0 ? ", " : "", + enum_values[i].string_value); + } + error.SetErrorString(strm.GetData()); + } + else + { + error.SetErrorString ("invalid enumeration argument"); + } + return fail_value; +} + +ScriptLanguage +Args::StringToScriptLanguage (const char *s, ScriptLanguage fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if ((::strcasecmp (s, "python") == 0) || + (::strcasecmp (s, "default") == 0 && eScriptLanguagePython == eScriptLanguageDefault)) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguagePython; + } + if (::strcasecmp (s, "none")) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguageNone; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +Error +Args::StringToFormat +( + const char *s, + lldb::Format &format, + size_t *byte_size_ptr +) +{ + format = eFormatInvalid; + Error error; + + if (s && s[0]) + { + if (byte_size_ptr) + { + if (isdigit (s[0])) + { + char *format_char = NULL; + 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().c_str()); + } + + if (error.Fail()) + return error; + } + else + { + error.SetErrorStringWithFormat("%s option string", s ? "empty" : "invalid"); + } + return error; +} + +lldb::Encoding +Args::StringToEncoding (const char *s, lldb::Encoding fail_value) +{ + if (s && s[0]) + { + if (strcmp(s, "uint") == 0) + return eEncodingUint; + else if (strcmp(s, "sint") == 0) + return eEncodingSint; + else if (strcmp(s, "ieee754") == 0) + return eEncodingIEEE754; + else if (strcmp(s, "vector") == 0) + return eEncodingVector; + } + return fail_value; +} + +uint32_t +Args::StringToGenericRegister (const char *s) +{ + if (s && s[0]) + { + if (strcmp(s, "pc") == 0) + return LLDB_REGNUM_GENERIC_PC; + else if (strcmp(s, "sp") == 0) + return LLDB_REGNUM_GENERIC_SP; + else if (strcmp(s, "fp") == 0) + return LLDB_REGNUM_GENERIC_FP; + else if (strcmp(s, "ra") == 0) + return LLDB_REGNUM_GENERIC_RA; + else if (strcmp(s, "flags") == 0) + return LLDB_REGNUM_GENERIC_FLAGS; + else if (strncmp(s, "arg", 3) == 0) + { + if (s[3] && s[4] == '\0') + { + switch (s[3]) + { + case '1': return LLDB_REGNUM_GENERIC_ARG1; + case '2': return LLDB_REGNUM_GENERIC_ARG2; + case '3': return LLDB_REGNUM_GENERIC_ARG3; + case '4': return LLDB_REGNUM_GENERIC_ARG4; + case '5': return LLDB_REGNUM_GENERIC_ARG5; + case '6': return LLDB_REGNUM_GENERIC_ARG6; + case '7': return LLDB_REGNUM_GENERIC_ARG7; + case '8': return LLDB_REGNUM_GENERIC_ARG8; + } + } + } + } + return LLDB_INVALID_REGNUM; +} + + +void +Args::LongestCommonPrefix (std::string &common_prefix) +{ + arg_sstr_collection::iterator pos, end = m_args.end(); + pos = m_args.begin(); + if (pos == end) + common_prefix.clear(); + else + common_prefix = (*pos); + + for (++pos; pos != end; ++pos) + { + size_t new_size = (*pos).size(); + + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); + + // Then trim it at the first disparity: + + for (size_t i = 0; i < common_prefix.size(); i++) + { + if ((*pos)[i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } + } + + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } +} + +size_t +Args::FindArgumentIndexForOption (struct option *long_options, int long_options_index) +{ + char short_buffer[3]; + char long_buffer[255]; + ::snprintf (short_buffer, sizeof (short_buffer), "-%c", long_options[long_options_index].val); + ::snprintf (long_buffer, sizeof (long_buffer), "--%s", long_options[long_options_index].name); + size_t end = GetArgumentCount (); + size_t idx = 0; + while (idx < end) + { + if ((::strncmp (GetArgumentAtIndex (idx), short_buffer, strlen (short_buffer)) == 0) + || (::strncmp (GetArgumentAtIndex (idx), long_buffer, strlen (long_buffer)) == 0)) + { + return idx; + } + ++idx; + } + + return end; +} + +bool +Args::IsPositionalArgument (const char *arg) +{ + if (arg == NULL) + return false; + + bool is_positional = true; + char *cptr = (char *) arg; + + if (cptr[0] == '%') + { + ++cptr; + while (isdigit (cptr[0])) + ++cptr; + if (cptr[0] != '\0') + is_positional = false; + } + else + is_positional = false; + + return is_positional; +} + +void +Args::ParseAliasOptions (Options &options, + CommandReturnObject &result, + OptionArgVector *option_arg_vector, + std::string &raw_input_string) +{ + StreamString sstr; + int i; + struct option *long_options = options.GetLongOptions(); + + if (long_options == NULL) + { + result.AppendError ("invalid long options"); + result.SetStatus (eReturnStatusFailed); + return; + } + + for (i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long_only (GetArgumentCount(), + GetArgumentVector(), + sstr.GetData(), + long_options, + &long_options_index); + + if (val == -1) + break; + + if (val == '?') + { + result.AppendError ("unknown or ambiguous option"); + result.SetStatus (eReturnStatusFailed); + break; + } + + if (val == 0) + continue; + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || 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) + { + StreamString option_str; + option_str.Printf ("-%c", val); + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (no_argument, ""))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + break; + case required_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (required_argument, + std::string (optarg)))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n", + option_str.GetData()); + result.SetStatus (eReturnStatusFailed); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (optional_argument, + std::string (optarg)))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + OptionArgValue (optional_argument, ""))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + break; + default: + result.AppendErrorWithFormat ("error with options table; invalid value in has_arg field for option '%c'.\n", val); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", val); + result.SetStatus (eReturnStatusFailed); + } + + if (long_options_index >= 0) + { + // 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 (long_options, long_options_index); + if (idx < GetArgumentCount()) + { + if (raw_input_string.size() > 0) + { + const char *tmp_arg = GetArgumentAtIndex (idx); + size_t pos = raw_input_string.find (tmp_arg); + if (pos != std::string::npos) + raw_input_string.erase (pos, strlen (tmp_arg)); + } + ReplaceArgumentAtIndex (idx, ""); + if ((long_options[long_options_index].has_arg != no_argument) + && (optarg != NULL) + && (idx+1 < GetArgumentCount()) + && (strcmp (optarg, GetArgumentAtIndex(idx+1)) == 0)) + { + if (raw_input_string.size() > 0) + { + const char *tmp_arg = GetArgumentAtIndex (idx+1); + size_t pos = raw_input_string.find (tmp_arg); + if (pos != std::string::npos) + raw_input_string.erase (pos, strlen (tmp_arg)); + } + ReplaceArgumentAtIndex (idx+1, ""); + } + } + } + + if (!result.Succeeded()) + break; + } +} + +void +Args::ParseArgsForCompletion +( + Options &options, + OptionElementVector &option_element_vector, + uint32_t cursor_index +) +{ + StreamString sstr; + struct option *long_options = options.GetLongOptions(); + option_element_vector.clear(); + + if (long_options == NULL) + { + return; + } + + // Leading : tells getopt to return a : for a missing option argument AND + // to suppress error messages. + + sstr << ":"; + for (int i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + opterr = 0; + + int val; + const OptionDefinition *opt_defs = options.GetDefinitions(); + + // Fooey... getopt_long_only permutes the GetArgumentVector to move the options to the front. + // So we have to build another Arg and pass that to getopt_long_only so it doesn't + // change the one we have. + + std::vector dummy_vec (GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); + + bool failed_once = false; + uint32_t dash_dash_pos = -1; + + while (1) + { + bool missing_argument = false; + int long_options_index = -1; + + val = ::getopt_long_only (dummy_vec.size() - 1, + (char *const *) &dummy_vec.front(), + sstr.GetData(), + 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 getopt_long_only 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 (optind < dummy_vec.size() - 1 + && (strcmp (dummy_vec[optind-1], "--") == 0)) + { + dash_dash_pos = optind - 1; + if (optind - 1 == cursor_index) + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDoubleDash, optind - 1, + OptionArgElement::eBareDoubleDash)); + continue; + } + else + break; + } + else + break; + } + else if (val == '?') + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, + OptionArgElement::eUnrecognizedArg)); + continue; + } + else if (val == 0) + { + continue; + } + else if (val == ':') + { + // This is a missing argument. + val = optopt; + missing_argument = true; + } + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || 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 (int i = 0; ; i++) + { + if (opt_defs[i].short_option == 0) + break; + else if (opt_defs[i].short_option == val) + { + opt_defs_index = i; + break; + } + } + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 1, 0)); + break; + case required_argument: + if (optarg != NULL) + { + int arg_index; + if (missing_argument) + arg_index = -1; + else + arg_index = optind - 1; + + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, arg_index)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 1, -1)); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, optind - 2, optind - 1)); + } + break; + default: + // The options table is messed up. Here we'll just continue + option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, + OptionArgElement::eUnrecognizedArg)); + break; + } + } + else + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eUnrecognizedArg, optind - 1, + 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 getopt_long_only just ignores + // an isolated "-". So we have to look it up by hand here. We only care if it is AT the cursor position. + + if ((dash_dash_pos == -1 || cursor_index < dash_dash_pos) + && strcmp (GetArgumentAtIndex(cursor_index), "-") == 0) + { + option_element_vector.push_back (OptionArgElement (OptionArgElement::eBareDash, cursor_index, + OptionArgElement::eBareDash)); + + } +} + +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, NULL, 8); + if (octal_value <= UINT8_MAX) + { + dst.append(1, (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, NULL, 16); + if (hex_value <= UINT8_MAX) + dst.append (1, (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 (isprint8(*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; + } + } + } + } +} + diff --git a/source/Interpreter/CommandHistory.cpp b/source/Interpreter/CommandHistory.cpp new file mode 100644 index 00000000000..33971e3959c --- /dev/null +++ b/source/Interpreter/CommandHistory.cpp @@ -0,0 +1,143 @@ +//===-- CommandHistory.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandHistory.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + + +CommandHistory::CommandHistory () : + m_mutex(Mutex::eMutexTypeRecursive), + m_history() +{} + +CommandHistory::~CommandHistory () +{} + +size_t +CommandHistory::GetSize () const +{ + Mutex::Locker locker(m_mutex); + return m_history.size(); +} + +bool +CommandHistory::IsEmpty () const +{ + Mutex::Locker locker(m_mutex); + return m_history.empty(); +} + +const char* +CommandHistory::FindString (const char* input_str) const +{ + Mutex::Locker locker(m_mutex); + if (!input_str) + return NULL; + if (input_str[0] != g_repeat_char) + return NULL; + if (input_str[1] == '-') + { + bool success; + size_t idx = Args::StringToUInt32 (input_str+2, 0, 0, &success); + if (!success) + return NULL; + if (idx > m_history.size()) + return NULL; + idx = m_history.size() - idx; + return m_history[idx].c_str(); + + } + else if (input_str[1] == g_repeat_char) + { + if (m_history.empty()) + return NULL; + else + return m_history.back().c_str(); + } + else + { + bool success; + uint32_t idx = Args::StringToUInt32 (input_str+1, 0, 0, &success); + if (!success) + return NULL; + if (idx >= m_history.size()) + return NULL; + return m_history[idx].c_str(); + } +} + +const char* +CommandHistory::GetStringAtIndex (size_t idx) const +{ + Mutex::Locker locker(m_mutex); + if (idx < m_history.size()) + return m_history[idx].c_str(); + return NULL; +} + +const char* +CommandHistory::operator [] (size_t idx) const +{ + return GetStringAtIndex(idx); +} + +const char* +CommandHistory::GetRecentmostString () const +{ + Mutex::Locker locker(m_mutex); + if (m_history.empty()) + return NULL; + return m_history.back().c_str(); +} + +void +CommandHistory::AppendString (const std::string& str, + bool reject_if_dupe) +{ + Mutex::Locker locker(m_mutex); + if (reject_if_dupe) + { + if (!m_history.empty()) + { + if (str == m_history.back()) + return; + } + } + m_history.push_back(std::string(str)); +} + +void +CommandHistory::Clear () +{ + Mutex::Locker locker(m_mutex); + m_history.clear(); +} + +void +CommandHistory::Dump (Stream& stream, + size_t start_idx, + size_t stop_idx) const +{ + Mutex::Locker locker(m_mutex); + stop_idx = std::min(stop_idx, m_history.size() - 1); + 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 ("%4zu: %s\n", counter, hist_item.c_str()); + } + } +} diff --git a/source/Interpreter/CommandInterpreter.cpp b/source/Interpreter/CommandInterpreter.cpp new file mode 100644 index 00000000000..db2f2fafbef --- /dev/null +++ b/source/Interpreter/CommandInterpreter.cpp @@ -0,0 +1,2882 @@ +//===-- CommandInterpreter.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include +#include + +#include +#include + +#include "CommandObjectScript.h" +#include "lldb/Interpreter/CommandObjectRegexCommand.h" + +#include "../Commands/CommandObjectApropos.h" +#include "../Commands/CommandObjectArgs.h" +#include "../Commands/CommandObjectBreakpoint.h" +#include "../Commands/CommandObjectDisassemble.h" +#include "../Commands/CommandObjectExpression.h" +#include "../Commands/CommandObjectFrame.h" +#include "../Commands/CommandObjectHelp.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/CommandObjectSettings.h" +#include "../Commands/CommandObjectSource.h" +#include "../Commands/CommandObjectCommands.h" +#include "../Commands/CommandObjectSyntax.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/InputReader.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/Timer.h" + +#include "lldb/Host/Host.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + + +#include "lldb/Target/Process.h" +#include "lldb/Target/Thread.h" +#include "lldb/Target/TargetList.h" + +#include "lldb/Utility/CleanUp.h" + +using namespace lldb; +using namespace lldb_private; + + +static PropertyDefinition +g_properties[] = +{ + { "expand-regex-aliases", OptionValue::eTypeBoolean, true, false, NULL, NULL, "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, true, true, NULL, NULL, "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, true, true, NULL, NULL, "If true, LLDB will stop running a 'command source' script upon encountering an error." }, + { NULL , OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } +}; + +enum +{ + ePropertyExpandRegexAliases = 0, + ePropertyPromptOnQuit = 1, + ePropertyStopCmdSourceOnError = 2 +}; + +ConstString & +CommandInterpreter::GetStaticBroadcasterClass () +{ + static ConstString class_name ("lldb.commandInterpreter"); + return class_name; +} + +CommandInterpreter::CommandInterpreter +( + Debugger &debugger, + ScriptLanguage script_language, + bool synchronous_execution +) : + Broadcaster (&debugger, "lldb.command-interpreter"), + Properties(OptionValuePropertiesSP(new OptionValueProperties(ConstString("interpreter")))), + m_debugger (debugger), + m_synchronous_execution (synchronous_execution), + m_skip_lldbinit_files (false), + m_skip_app_init_files (false), + m_script_interpreter_ap (), + m_comment_char ('#'), + m_batch_command_mode (false), + m_truncation_warning(eNoTruncation), + m_command_source_depth (0) +{ + debugger.SetScriptLanguage (script_language); + 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 (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +CommandInterpreter::GetPromptOnQuit () const +{ + const uint32_t idx = ePropertyPromptOnQuit; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +CommandInterpreter::GetStopCmdSourceOnError () const +{ + const uint32_t idx = ePropertyStopCmdSourceOnError; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +void +CommandInterpreter::Initialize () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + CommandReturnObject result; + + LoadCommandDictionary (); + + // 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); + } + + 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); + + cmd_obj_sp = GetCommandSPExact ("_regexp-tbreak",false); + if (cmd_obj_sp) + AddAlias ("tbreak", cmd_obj_sp); + + 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); + } + + 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-list", false); + if (cmd_obj_sp) + { + AddAlias ("l", cmd_obj_sp); + AddAlias ("list", cmd_obj_sp); + } + + cmd_obj_sp = GetCommandSPExact ("_regexp-env", false); + if (cmd_obj_sp) + { + AddAlias ("env", cmd_obj_sp); + } + + 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); + + cmd_obj_sp = GetCommandSPExact ("_regexp-down", false); + if (cmd_obj_sp) + AddAlias ("down", cmd_obj_sp); + + cmd_obj_sp = GetCommandSPExact ("_regexp-display", false); + if (cmd_obj_sp) + AddAlias ("display", cmd_obj_sp); + + 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); + + cmd_obj_sp = GetCommandSPExact ("_regexp-bt", false); + if (cmd_obj_sp) + AddAlias ("bt", cmd_obj_sp); + + 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); + + + OptionArgVectorSP alias_arguments_vector_sp (new OptionArgVector); + + cmd_obj_sp = GetCommandSPExact ("expression", false); + if (cmd_obj_sp) + { + ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp); + AddAlias ("p", cmd_obj_sp); + AddAlias ("print", cmd_obj_sp); + AddAlias ("call", cmd_obj_sp); + AddOrReplaceAliasOptions ("p", alias_arguments_vector_sp); + AddOrReplaceAliasOptions ("print", alias_arguments_vector_sp); + AddOrReplaceAliasOptions ("call", alias_arguments_vector_sp); + + alias_arguments_vector_sp.reset (new OptionArgVector); + ProcessAliasOptionsArgs (cmd_obj_sp, "-O -- ", alias_arguments_vector_sp); + AddAlias ("po", cmd_obj_sp); + AddOrReplaceAliasOptions ("po", alias_arguments_vector_sp); + } + + 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.reset (new OptionArgVector); +#if defined (__arm__) + ProcessAliasOptionsArgs (cmd_obj_sp, "--", alias_arguments_vector_sp); +#else + ProcessAliasOptionsArgs (cmd_obj_sp, "--shell=/bin/bash --", alias_arguments_vector_sp); +#endif + AddAlias ("r", cmd_obj_sp); + AddAlias ("run", cmd_obj_sp); + AddOrReplaceAliasOptions ("r", alias_arguments_vector_sp); + AddOrReplaceAliasOptions ("run", alias_arguments_vector_sp); + } + + 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) + { + alias_arguments_vector_sp.reset (new OptionArgVector); + ProcessAliasOptionsArgs (cmd_obj_sp, "--func-regex %1", alias_arguments_vector_sp); + AddAlias ("rbreak", cmd_obj_sp); + AddOrReplaceAliasOptions("rbreak", alias_arguments_vector_sp); + } +} + +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 () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __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["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["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["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["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)); + + const char *break_regexes[][2] = {{"^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "breakpoint set --file '%1' --line %2"}, + {"^([[: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 = sizeof break_regexes/sizeof(char *[2]); + + std::unique_ptr + break_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-break", + "Set a breakpoint using a regular expression to specify the location, where is in decimal and
is in hex.", + "_regexp-break [:]\n_regexp-break []\n_regexp-break [
]\n_regexp-break <...>", + 2, + CommandCompletions::eSymbolCompletion | + CommandCompletions::eSourceFileCompletion)); + + if (break_regex_cmd_ap.get()) + { + bool success = true; + for (size_t i = 0; i < num_regexes; i++) + { + success = break_regex_cmd_ap->AddRegexCommand (break_regexes[i][0], break_regexes[i][1]); + if (!success) + break; + } + success = break_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); + + if (success) + { + CommandObjectSP break_regex_cmd_sp(break_regex_cmd_ap.release()); + m_command_dict[break_regex_cmd_sp->GetCommandName ()] = break_regex_cmd_sp; + } + } + + std::unique_ptr + tbreak_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-tbreak", + "Set a one shot breakpoint using a regular expression to specify the location, where is in decimal and
is in hex.", + "_regexp-tbreak [:]\n_regexp-break []\n_regexp-break [
]\n_regexp-break <...>", + 2, + CommandCompletions::eSymbolCompletion | + CommandCompletions::eSourceFileCompletion)); + + if (tbreak_regex_cmd_ap.get()) + { + 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"); + assert (num_printed < 1024); + success = tbreak_regex_cmd_ap->AddRegexCommand (break_regexes[i][0], buffer); + if (!success) + break; + } + success = tbreak_regex_cmd_ap->AddRegexCommand("^$", "breakpoint list --full"); + + if (success) + { + CommandObjectSP tbreak_regex_cmd_sp(tbreak_regex_cmd_ap.release()); + m_command_dict[tbreak_regex_cmd_sp->GetCommandName ()] = tbreak_regex_cmd_sp; + } + } + + std::unique_ptr + attach_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-attach", + "Attach to a process id if in decimal, otherwise treat the argument as a process name to attach to.", + "_regexp-attach []\n_regexp-attach []", + 2)); + if (attach_regex_cmd_ap.get()) + { + if (attach_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "process attach --pid %1") && + attach_regex_cmd_ap->AddRegexCommand("^(-.*|.* -.*)$", "process attach %1") && // Any options that are specified get passed to 'process attach' + attach_regex_cmd_ap->AddRegexCommand("^(.+)$", "process attach --name '%1'") && + attach_regex_cmd_ap->AddRegexCommand("^$", "process attach")) + { + CommandObjectSP attach_regex_cmd_sp(attach_regex_cmd_ap.release()); + m_command_dict[attach_regex_cmd_sp->GetCommandName ()] = attach_regex_cmd_sp; + } + } + + std::unique_ptr + down_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-down", + "Go down \"n\" frames in the stack (1 frame by default).", + "_regexp-down [n]", 2)); + if (down_regex_cmd_ap.get()) + { + if (down_regex_cmd_ap->AddRegexCommand("^$", "frame select -r -1") && + down_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r -%1")) + { + CommandObjectSP down_regex_cmd_sp(down_regex_cmd_ap.release()); + m_command_dict[down_regex_cmd_sp->GetCommandName ()] = down_regex_cmd_sp; + } + } + + std::unique_ptr + up_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-up", + "Go up \"n\" frames in the stack (1 frame by default).", + "_regexp-up [n]", 2)); + if (up_regex_cmd_ap.get()) + { + if (up_regex_cmd_ap->AddRegexCommand("^$", "frame select -r 1") && + up_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "frame select -r %1")) + { + CommandObjectSP up_regex_cmd_sp(up_regex_cmd_ap.release()); + m_command_dict[up_regex_cmd_sp->GetCommandName ()] = up_regex_cmd_sp; + } + } + + std::unique_ptr + display_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-display", + "Add an expression evaluation stop-hook.", + "_regexp-display expression", 2)); + if (display_regex_cmd_ap.get()) + { + if (display_regex_cmd_ap->AddRegexCommand("^(.+)$", "target stop-hook add -o \"expr -- %1\"")) + { + CommandObjectSP display_regex_cmd_sp(display_regex_cmd_ap.release()); + m_command_dict[display_regex_cmd_sp->GetCommandName ()] = display_regex_cmd_sp; + } + } + + std::unique_ptr + undisplay_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-undisplay", + "Remove an expression evaluation stop-hook.", + "_regexp-undisplay stop-hook-number", 2)); + if (undisplay_regex_cmd_ap.get()) + { + if (undisplay_regex_cmd_ap->AddRegexCommand("^([0-9]+)$", "target stop-hook delete %1")) + { + CommandObjectSP undisplay_regex_cmd_sp(undisplay_regex_cmd_ap.release()); + m_command_dict[undisplay_regex_cmd_sp->GetCommandName ()] = undisplay_regex_cmd_sp; + } + } + + std::unique_ptr + connect_gdb_remote_cmd_ap(new CommandObjectRegexCommand (*this, + "gdb-remote", + "Connect to a remote GDB server. If no hostname is provided, localhost is assumed.", + "gdb-remote [:]", 2)); + if (connect_gdb_remote_cmd_ap.get()) + { + if (connect_gdb_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin gdb-remote connect://%1") && + connect_gdb_remote_cmd_ap->AddRegexCommand("^([[:digit:]]+)$", "process connect --plugin gdb-remote connect://localhost:%1")) + { + CommandObjectSP command_sp(connect_gdb_remote_cmd_ap.release()); + m_command_dict[command_sp->GetCommandName ()] = command_sp; + } + } + + std::unique_ptr + connect_kdp_remote_cmd_ap(new CommandObjectRegexCommand (*this, + "kdp-remote", + "Connect to a remote KDP server. udp port 41139 is the default port number.", + "kdp-remote [:]", 2)); + if (connect_kdp_remote_cmd_ap.get()) + { + if (connect_kdp_remote_cmd_ap->AddRegexCommand("^([^:]+:[[:digit:]]+)$", "process connect --plugin kdp-remote udp://%1") && + connect_kdp_remote_cmd_ap->AddRegexCommand("^(.+)$", "process connect --plugin kdp-remote udp://%1:41139")) + { + CommandObjectSP command_sp(connect_kdp_remote_cmd_ap.release()); + m_command_dict[command_sp->GetCommandName ()] = command_sp; + } + } + + std::unique_ptr + bt_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-bt", + "Show a backtrace. An optional argument is accepted; if that argument is a number, it specifies the number of frames to display. If that argument is 'all', full backtraces of all threads are displayed.", + "bt [|all]", 2)); + if (bt_regex_cmd_ap.get()) + { + // accept but don't document "bt -c " -- 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_ap->AddRegexCommand("^([[:digit:]]+)$", "thread backtrace -c %1") && + bt_regex_cmd_ap->AddRegexCommand("^-c ([[:digit:]]+)$", "thread backtrace -c %1") && + bt_regex_cmd_ap->AddRegexCommand("^all$", "thread backtrace all") && + bt_regex_cmd_ap->AddRegexCommand("^$", "thread backtrace")) + { + CommandObjectSP command_sp(bt_regex_cmd_ap.release()); + m_command_dict[command_sp->GetCommandName ()] = command_sp; + } + } + + std::unique_ptr + list_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-list", + "Implements the GDB 'list' command in all of its forms except FILE:FUNCTION and maps them to the appropriate 'source list' commands.", + "_regexp-list []\n_regexp-attach [:]\n_regexp-attach [:]", + 2, + CommandCompletions::eSourceFileCompletion)); + if (list_regex_cmd_ap.get()) + { + if (list_regex_cmd_ap->AddRegexCommand("^([0-9]+)[[:space:]]*$", "source list --line %1") && + list_regex_cmd_ap->AddRegexCommand("^(.*[^[:space:]])[[:space:]]*:[[:space:]]*([[:digit:]]+)[[:space:]]*$", "source list --file '%1' --line %2") && + list_regex_cmd_ap->AddRegexCommand("^\\*?(0x[[:xdigit:]]+)[[:space:]]*$", "source list --address %1") && + list_regex_cmd_ap->AddRegexCommand("^-[[:space:]]*$", "source list --reverse") && + list_regex_cmd_ap->AddRegexCommand("^-([[:digit:]]+)[[:space:]]*$", "source list --reverse --count %1") && + list_regex_cmd_ap->AddRegexCommand("^(.+)$", "source list --name \"%1\"") && + list_regex_cmd_ap->AddRegexCommand("^$", "source list")) + { + CommandObjectSP list_regex_cmd_sp(list_regex_cmd_ap.release()); + m_command_dict[list_regex_cmd_sp->GetCommandName ()] = list_regex_cmd_sp; + } + } + + std::unique_ptr + env_regex_cmd_ap(new CommandObjectRegexCommand (*this, + "_regexp-env", + "Implements a shortcut to viewing and setting environment variables.", + "_regexp-env\n_regexp-env FOO=BAR", 2)); + if (env_regex_cmd_ap.get()) + { + if (env_regex_cmd_ap->AddRegexCommand("^$", "settings show target.env-vars") && + env_regex_cmd_ap->AddRegexCommand("^([A-Za-z_][A-Za-z_0-9]*=.*)$", "settings set target.env-vars %1")) + { + CommandObjectSP env_regex_cmd_sp(env_regex_cmd_ap.release()); + m_command_dict[env_regex_cmd_sp->GetCommandName ()] = env_regex_cmd_sp; + } + } + +} + +int +CommandInterpreter::GetCommandNamesMatchingPartialString (const char *cmd_str, bool include_aliases, + StringList &matches) +{ + CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_str, matches); + + if (include_aliases) + { + CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_str, matches); + } + + return matches.GetSize(); +} + +CommandObjectSP +CommandInterpreter::GetCommandSP (const char *cmd_cstr, bool include_aliases, bool exact, StringList *matches) +{ + CommandObject::CommandMap::iterator pos; + CommandObjectSP command_sp; + + std::string cmd(cmd_cstr); + + if (HasCommands()) + { + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + command_sp = pos->second; + } + + if (include_aliases && HasAliases()) + { + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + command_sp = pos->second; + } + + if (HasUserCommands()) + { + 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 == NULL) + 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 = CommandObject::AddNamesMatchingPartialString (m_command_dict, cmd_cstr, *matches); + } + + if (num_cmd_matches == 1) + { + cmd.assign(matches->GetStringAtIndex(0)); + pos = m_command_dict.find(cmd); + if (pos != m_command_dict.end()) + real_match_sp = pos->second; + } + + if (include_aliases && HasAliases()) + { + num_alias_matches = CommandObject::AddNamesMatchingPartialString (m_alias_dict, cmd_cstr, *matches); + + } + + if (num_alias_matches == 1) + { + cmd.assign(matches->GetStringAtIndex (num_cmd_matches)); + pos = m_alias_dict.find(cmd); + if (pos != m_alias_dict.end()) + alias_match_sp = pos->second; + } + + if (HasUserCommands()) + { + num_user_matches = CommandObject::AddNamesMatchingPartialString (m_user_dict, cmd_cstr, *matches); + } + + if (num_user_matches == 1) + { + cmd.assign (matches->GetStringAtIndex (num_cmd_matches + num_alias_matches)); + + 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_cstr); + } + + + return command_sp; +} + +bool +CommandInterpreter::AddCommand (const char *name, const lldb::CommandObjectSP &cmd_sp, bool can_replace) +{ + if (name && name[0]) + { + std::string name_sstr(name); + bool found = (m_command_dict.find (name_sstr) != m_command_dict.end()); + if (found && !can_replace) + return false; + if (found && m_command_dict[name_sstr]->IsRemovable() == false) + return false; + m_command_dict[name_sstr] = cmd_sp; + return true; + } + return false; +} + +bool +CommandInterpreter::AddUserCommand (std::string name, + const lldb::CommandObjectSP &cmd_sp, + bool can_replace) +{ + if (!name.empty()) + { + + const char* name_cstr = name.c_str(); + + // do not allow replacement of internal commands + if (CommandExists(name_cstr)) + { + if (can_replace == false) + return false; + if (m_command_dict[name]->IsRemovable() == false) + return false; + } + + if (UserCommandExists(name_cstr)) + { + if (can_replace == false) + return false; + if (m_user_dict[name]->IsRemovable() == false) + return false; + } + + m_user_dict[name] = cmd_sp; + return true; + } + return false; +} + +CommandObjectSP +CommandInterpreter::GetCommandSPExact (const char *cmd_cstr, bool include_aliases) +{ + Args cmd_words (cmd_cstr); // Break up the command string into words, in case it's a multi-word command. + CommandObjectSP ret_val; // Possibly empty return value. + + if (cmd_cstr == NULL) + return ret_val; + + if (cmd_words.GetArgumentCount() == 1) + return GetCommandSP(cmd_cstr, include_aliases, true, NULL); + 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 (cmd_words.GetArgumentAtIndex (0), include_aliases, true, NULL); + if (cmd_obj_sp.get() != NULL) + { + // 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() == NULL) + // 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::GetCommandObjectExact (const char *cmd_cstr, bool include_aliases) +{ + return GetCommandSPExact (cmd_cstr, include_aliases).get(); +} + +CommandObject * +CommandInterpreter::GetCommandObject (const char *cmd_cstr, StringList *matches) +{ + CommandObject *command_obj = GetCommandSP (cmd_cstr, false, true, matches).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_cstr, true, true, matches).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_cstr, false, false, NULL).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()); + return command_obj; + } + + return GetCommandSP(cmd_cstr, true, false, matches).get(); +} + +bool +CommandInterpreter::CommandExists (const char *cmd) +{ + return m_command_dict.find(cmd) != m_command_dict.end(); +} + +bool +CommandInterpreter::ProcessAliasOptionsArgs (lldb::CommandObjectSP &cmd_obj_sp, + const char *options_args, + OptionArgVectorSP &option_arg_vector_sp) +{ + bool success = true; + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + if (!options_args || (strlen (options_args) < 1)) + return true; + + std::string options_string (options_args); + Args args (options_args); + 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. + options->NotifyOptionParsingStarting (); + args.Unshift ("dummy_arg"); + args.ParseAliasOptions (*options, result, option_arg_vector, options_string); + args.Shift (); + if (result.Succeeded()) + 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->push_back (OptionArgPair ("", + OptionArgValue (-1, + options_string))); + else + { + const size_t argc = args.GetArgumentCount(); + for (size_t i = 0; i < argc; ++i) + if (strcmp (args.GetArgumentAtIndex (i), "") != 0) + option_arg_vector->push_back + (OptionArgPair ("", + OptionArgValue (-1, + std::string (args.GetArgumentAtIndex (i))))); + } + } + + return success; +} + +bool +CommandInterpreter::GetAliasFullName (const char *cmd, std::string &full_name) +{ + 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 = CommandObject::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, ®ular_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 (const char *cmd) +{ + return m_alias_dict.find(cmd) != m_alias_dict.end(); +} + +bool +CommandInterpreter::UserCommandExists (const char *cmd) +{ + return m_user_dict.find(cmd) != m_user_dict.end(); +} + +void +CommandInterpreter::AddAlias (const char *alias_name, CommandObjectSP& command_obj_sp) +{ + command_obj_sp->SetIsAlias (true); + m_alias_dict[alias_name] = command_obj_sp; +} + +bool +CommandInterpreter::RemoveAlias (const char *alias_name) +{ + CommandObject::CommandMap::iterator pos = m_alias_dict.find(alias_name); + if (pos != m_alias_dict.end()) + { + m_alias_dict.erase(pos); + return true; + } + return false; +} +bool +CommandInterpreter::RemoveUser (const char *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::GetAliasHelp (const char *alias_name, const char *command_name, StreamString &help_string) +{ + help_string.Printf ("'%s", command_name); + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp) + { + OptionArgVector *options = option_arg_vector_sp.get(); + for (size_t i = 0; i < options->size(); ++i) + { + OptionArgPair cur_option = (*options)[i]; + std::string opt = cur_option.first; + OptionArgValue value_pair = cur_option.second; + std::string value = value_pair.second; + if (opt.compare("") == 0) + { + help_string.Printf (" %s", value.c_str()); + } + else + { + help_string.Printf (" %s", opt.c_str()); + if ((value.compare ("") != 0) + && (value.compare ("first.size(); + if (max_len < len) + max_len = len; + } + return max_len; +} + +void +CommandInterpreter::GetHelp (CommandReturnObject &result, + uint32_t cmd_types) +{ + CommandObject::CommandMap::const_iterator pos; + size_t max_len = FindLongestCommandWord (m_command_dict); + + if ( (cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin ) + { + + result.AppendMessage("The following is a list of built-in, permanent debugger commands:"); + result.AppendMessage(""); + + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + { + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(), + max_len); + } + result.AppendMessage(""); + + } + + if (!m_alias_dict.empty() && ( (cmd_types & eCommandTypesAliases) == eCommandTypesAliases )) + { + result.AppendMessage("The following is a list of your current command abbreviations " + "(see 'help command alias' for more info):"); + result.AppendMessage(""); + max_len = FindLongestCommandWord (m_alias_dict); + + for (pos = m_alias_dict.begin(); pos != m_alias_dict.end(); ++pos) + { + StreamString sstr; + StreamString translation_and_help; + std::string entry_name = pos->first; + std::string second_entry = pos->second.get()->GetCommandName(); + GetAliasHelp (pos->first.c_str(), pos->second->GetCommandName(), sstr); + + translation_and_help.Printf ("(%s) %s", sstr.GetData(), pos->second->GetHelp()); + OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", + translation_and_help.GetData(), max_len); + } + result.AppendMessage(""); + } + + if (!m_user_dict.empty() && ( (cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef )) + { + result.AppendMessage ("The following is a list of your 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.c_str(), "--", pos->second->GetHelp(), + max_len); + } + result.AppendMessage(""); + } + + result.AppendMessage("For more information on any particular command, try 'help '."); +} + +CommandObject * +CommandInterpreter::GetCommandObjectForCommand (std::string &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 = NULL; + std::string white_space (" \t\v"); + size_t start = command_string.find_first_not_of (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 (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 == NULL) + // 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.c_str()); + 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 donee + 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 (white_space, end); + } + else + // Unable to find any more words. + done = true; + } + + if (end == command_string.size()) + command_string.clear(); + else + command_string = command_string.substr(end); + + return cmd_obj; +} + +static const char *k_white_space = " \t\v"; +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 "e_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 (const char *alias_name, + std::string &raw_input_string, + std::string &alias_result, + CommandReturnObject &result) +{ + CommandObject *alias_cmd_obj = NULL; + Args cmd_args (raw_input_string.c_str()); + alias_cmd_obj = GetCommandObject (alias_name); + StreamString result_str; + + if (alias_cmd_obj) + { + std::string alias_name_str = alias_name; + if ((cmd_args.GetArgumentCount() == 0) + || (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0)) + cmd_args.Unshift (alias_name); + + result_str.Printf ("%s", alias_cmd_obj->GetCommandName ()); + OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name); + + if (option_arg_vector_sp.get()) + { + OptionArgVector *option_arg_vector = option_arg_vector_sp.get(); + + for (size_t i = 0; i < option_arg_vector->size(); ++i) + { + OptionArgPair option_pair = (*option_arg_vector)[i]; + OptionArgValue value_pair = option_pair.second; + int value_type = value_pair.first; + std::string option = option_pair.first; + std::string value = value_pair.second; + if (option.compare ("") == 0) + result_str.Printf (" %s", value.c_str()); + else + { + result_str.Printf (" %s", option.c_str()); + if (value_type != optional_argument) + result_str.Printf (" "); + if (value.compare ("") != 0) + { + int index = GetOptionArgumentPosition (value.c_str()); + if (index == 0) + result_str.Printf ("%s", value.c_str()); + else if (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 alias_cmd_obj; + } + 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.GetData(); + } + return alias_cmd_obj; +} + +Error +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 proprocessed is anyting 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` + Error error; // Error for any expressions that might not evaluate + size_t start_backtick; + size_t pos = 0; + while ((start_backtick = command.find ('`', pos)) != std::string::npos) + { + if (start_backtick > 0 && command[start_backtick-1] == '\\') + { + // The backtick was preceeded 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; + } + else + { + 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) + return error; + else if (end_backtick == expr_content_start) + { + // Empty expression (two backticks in a row) + command.erase (start_backtick, 2); + } + else + { + 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 = Host::GetDummyTarget(GetDebugger()).get(); + if (target) + { + ValueObjectSP expr_result_valobj_sp; + + EvaluateExpressionOptions options; + options.SetCoerceToId(false) + .SetUnwindOnError(true) + .SetIgnoreBreakpoints(true) + .SetKeepInMemory(false) + .SetRunOthers(true) + .SetTimeoutUsec(0); + + ExecutionResults expr_result = target->EvaluateExpression (expr_str.c_str(), + exe_ctx.GetFramePtr(), + expr_result_valobj_sp, + options); + + if (expr_result == eExecutionCompleted) + { + Scalar scalar; + 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.GetData(), value_string_size); + 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()); + } + } + else + { + error.SetErrorStringWithFormat("expression value didn't result in a scalar value for the expression '%s'", expr_str.c_str()); + } + } + else + { + if (expr_result_valobj_sp) + error = expr_result_valobj_sp->GetError(); + if (error.Success()) + { + + switch (expr_result) + { + case eExecutionSetupError: + error.SetErrorStringWithFormat("expression setup error for the expression '%s'", expr_str.c_str()); + break; + case eExecutionCompleted: + break; + case eExecutionDiscarded: + error.SetErrorStringWithFormat("expression discarded for the expression '%s'", expr_str.c_str()); + break; + case eExecutionInterrupted: + error.SetErrorStringWithFormat("expression interrupted for the expression '%s'", expr_str.c_str()); + break; + case eExecutionHitBreakpoint: + error.SetErrorStringWithFormat("expression hit breakpoint for the expression '%s'", expr_str.c_str()); + break; + case eExecutionTimedOut: + error.SetErrorStringWithFormat("expression timed out for the expression '%s'", expr_str.c_str()); + break; + } + } + } + } + } + if (error.Fail()) + 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) + +{ + + bool done = false; + CommandObject *cmd_obj = NULL; + bool wants_raw_input = false; + std::string command_string (command_line); + std::string original_command_string (command_line); + + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMANDS)); + Host::SetCrashDescriptionWithFormat ("HandleCommand(command = \"%s\")", command_line); + + // Make a scoped cleanup object that will clear the crash description string + // on exit of this function. + lldb_utility::CleanUp crash_description_cleanup(NULL, Host::SetCrashDescription); + + if (log) + log->Printf ("Processing command: %s", command_line); + + Timer scoped_timer (__PRETTY_FUNCTION__, "Handling command: %s.", command_line); + + if (!no_context_switching) + UpdateExecutionContext (override_context); + + 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) + { + const char *history_string = m_command_history.FindString(command_string.c_str() + non_space); + if (history_string == NULL) + { + result.AppendErrorWithFormat ("Could not find entry: %s in history", command_string.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + add_to_history = false; + command_string = history_string; + original_command_string = history_string; + } + } + + 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; + } + + + Error 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, etc. we need to figure out what the real/final command object + // is for the specified command, and whether or not it wants raw input. 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 & replacement, resulting in three things: 1). the command + // object whose Execute method will actually be called; 2). a revised command string, with all substitutions & + // replacements taken care of; 3). whether or not the Execute function wants raw input or not. + + StreamString revised_command_line; + size_t actual_cmd_name_len = 0; + std::string next_word; + StringList matches; + while (!done) + { + char quote_char = '\0'; + std::string suffix; + ExtractCommand (command_string, next_word, suffix, quote_char); + if (cmd_obj == NULL) + { + std::string full_name; + if (GetAliasFullName(next_word.c_str(), full_name)) + { + std::string alias_result; + cmd_obj = BuildAliasResult (full_name.c_str(), command_string, 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 = strlen (cmd_obj->GetCommandName()); + } + } + else + { + cmd_obj = GetCommandObject (next_word.c_str(), &matches); + if (cmd_obj) + { + actual_cmd_name_len += next_word.length(); + revised_command_line.Printf ("%s", next_word.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) + { + actual_cmd_name_len += next_word.length() + 1; + revised_command_line.Printf (" %s", next_word.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 == NULL) + { + 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().c_str()); + } else { + // We didn't have only one match, otherwise we wouldn't get here. + assert(num_matches == 0); + result.AppendErrorWithFormat ("'%s' is not a valid command.\n", next_word.c_str()); + } + result.SetStatus (eReturnStatusFailed); + return false; + } + + 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(), + next_word.empty() ? "" : next_word.c_str(), + next_word.empty() ? " -- " : " ", + suffix.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + 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); + + bool inserted = false; + 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); + inserted = true; + } + + if (!inserted) + 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()); + result.SetStatus (eReturnStatusFailed); + return false; + } + } + break; + + default: + result.AppendErrorWithFormat ("unknown command shorthand suffix: '%s'\n", + suffix.c_str()); + result.SetStatus (eReturnStatusFailed); + return false; + + } + } + } + if (command_string.length() == 0) + done = true; + + } + + if (!command_string.empty()) + revised_command_line.Printf (" %s", command_string.c_str()); + + // End of Phase 1. + // At this point cmd_obj should contain the CommandObject whose Execute method will be called, if the command + // specified was valid; revised_command_line contains the complete command line (including command name(s)), + // fully translated with all substitutions & translations taken care of (still in raw text format); and + // wants_raw_input specifies whether the Execute method expects raw input or not. + + + if (log) + { + log->Printf ("HandleCommand, cmd_obj : '%s'", cmd_obj ? cmd_obj->GetCommandName() : ""); + log->Printf ("HandleCommand, revised_command_line: '%s'", revised_command_line.GetData()); + 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 != NULL) + { + if (add_to_history) + { + Args command_args (revised_command_line.GetData()); + const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0); + if (repeat_command != NULL) + m_repeat_command.assign(repeat_command); + else + m_repeat_command.assign(original_command_string.c_str()); + + m_command_history.AppendString (original_command_string); + } + + command_string = revised_command_line.GetData(); + std::string command_name (cmd_obj->GetCommandName()); + std::string remainder; + if (actual_cmd_name_len < command_string.length()) + remainder = command_string.substr (actual_cmd_name_len); // Note: 'actual_cmd_name_len' may be considerably shorter + // than cmd_obj->GetCommandName(), because name completion + // allows users to enter short versions of the names, + // e.g. 'br s' for 'breakpoint set'. + + // Remove any initial spaces + std::string white_space (" \t\v"); + size_t pos = remainder.find_first_not_of (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); + } + else + { + // We didn't find the first command object, so complete the first argument. + Args command_args (revised_command_line.GetData()); + StringList matches; + int num_matches; + int cursor_index = 0; + int cursor_char_position = strlen (command_args.GetArgumentAtIndex(0)); + bool word_complete; + num_matches = HandleCompletionMatches (command_args, + cursor_index, + cursor_char_position, + 0, + -1, + word_complete, + matches); + + if (num_matches > 0) + { + std::string error_msg; + error_msg.assign ("ambiguous command '"); + error_msg.append(command_args.GetArgumentAtIndex(0)); + error_msg.append ("'."); + + error_msg.append (" Possible completions:"); + for (int i = 0; i < num_matches; i++) + { + error_msg.append ("\n\t"); + error_msg.append (matches.GetStringAtIndex (i)); + } + error_msg.append ("\n"); + result.AppendRawError (error_msg.c_str()); + } + else + result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_args.GetArgumentAtIndex (0)); + + result.SetStatus (eReturnStatusFailed); + } + + if (log) + log->Printf ("HandleCommand, command %s", (result.Succeeded() ? "succeeded" : "did not succeed")); + + return result.Succeeded(); +} + +int +CommandInterpreter::HandleCompletionMatches (Args &parsed_line, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + int num_command_matches = 0; + bool look_for_subcommand = false; + + // For any of the command completions a unique match will be a complete word. + word_complete = true; + + if (cursor_index == -1) + { + // We got nothing on the command line, so return the list of commands + bool include_aliases = true; + num_command_matches = GetCommandNamesMatchingPartialString ("", include_aliases, matches); + } + else if (cursor_index == 0) + { + // The cursor is in the first argument, so just do a lookup in the dictionary. + CommandObject *cmd_obj = GetCommandObject (parsed_line.GetArgumentAtIndex(0), &matches); + num_command_matches = matches.GetSize(); + + if (num_command_matches == 1 + && cmd_obj && cmd_obj->IsMultiwordObject() + && matches.GetStringAtIndex(0) != NULL + && strcmp (parsed_line.GetArgumentAtIndex(0), matches.GetStringAtIndex(0)) == 0) + { + look_for_subcommand = true; + num_command_matches = 0; + matches.DeleteStringAtIndex(0); + parsed_line.AppendArgument (""); + cursor_index++; + cursor_char_position = 0; + } + } + + if (cursor_index > 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 (parsed_line.GetArgumentAtIndex(0)); + if (command_object == NULL) + { + return 0; + } + else + { + parsed_line.Shift(); + cursor_index--; + num_command_matches = command_object->HandleCompletion (parsed_line, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + } + } + + 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) +{ + // 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. + + Args parsed_line(current_line, last_char - current_line); + Args partial_parsed_line(current_line, cursor - current_line); + + // 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 = parsed_line.GetArgumentAtIndex(0); + if (first_arg) + { + if (first_arg[0] == m_comment_char) + return 0; + else if (first_arg[0] == CommandHistory::g_repeat_char) + { + const char *history_string = m_command_history.FindString (first_arg); + if (history_string != NULL) + { + matches.Clear(); + matches.InsertStringAtIndex(0, history_string); + return -2; + } + else + return 0; + + } + } + + + int num_args = partial_parsed_line.GetArgumentCount(); + int cursor_index = partial_parsed_line.GetArgumentCount() - 1; + int cursor_char_position; + + if (cursor_index == -1) + cursor_char_position = 0; + else + cursor_char_position = strlen (partial_parsed_line.GetArgumentAtIndex(cursor_index)); + + if (cursor > current_line && 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 = partial_parsed_line.GetArgumentAtIndex(cursor_index); + if (cursor_char_position == 0 || current_elem[cursor_char_position - 1] != ' ') + { + parsed_line.InsertArgumentAtIndex(cursor_index + 1, "", '"'); + cursor_index++; + cursor_char_position = 0; + } + } + + int num_command_matches; + + matches.Clear(); + + // Only max_return_elements == -1 is supported at present: + assert (max_return_elements == -1); + bool word_complete; + num_command_matches = HandleCompletionMatches (parsed_line, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + + if (num_command_matches <= 0) + return num_command_matches; + + if (num_args == 0) + { + // If we got an empty string, insert nothing. + matches.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; + if (cursor_index >= 0) + command_partial_str.assign(parsed_line.GetArgumentAtIndex(cursor_index), + parsed_line.GetArgumentAtIndex(cursor_index) + cursor_char_position); + + std::string common_prefix; + matches.LongestCommonPrefix (common_prefix); + const size_t partial_name_len = command_partial_str.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_command_matches == 1 && word_complete) + { + char quote_char = parsed_line.GetArgumentQuoteCharAtIndex(cursor_index); + if (quote_char != '\0') + common_prefix.push_back(quote_char); + + common_prefix.push_back(' '); + } + common_prefix.erase (0, partial_name_len); + matches.InsertStringAtIndex(0, common_prefix.c_str()); + } + return num_command_matches; +} + + +CommandInterpreter::~CommandInterpreter () +{ +} + +const char * +CommandInterpreter::GetPrompt () +{ + return m_debugger.GetPrompt(); +} + +void +CommandInterpreter::SetPrompt (const char *new_prompt) +{ + m_debugger.SetPrompt (new_prompt); +} + +size_t +CommandInterpreter::GetConfirmationInputReaderCallback +( + void *baton, + InputReader &reader, + lldb::InputReaderAction action, + const char *bytes, + size_t bytes_len +) +{ + File &out_file = reader.GetDebugger().GetOutputFile(); + bool *response_ptr = (bool *) baton; + + switch (action) + { + case eInputReaderActivate: + if (out_file.IsValid()) + { + if (reader.GetPrompt()) + { + out_file.Printf ("%s", reader.GetPrompt()); + out_file.Flush (); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + if (out_file.IsValid() && reader.GetPrompt()) + { + out_file.Printf ("%s", reader.GetPrompt()); + out_file.Flush (); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + if (bytes_len == 0) + { + reader.SetIsDone(true); + } + else if (bytes[0] == 'y' || bytes[0] == 'Y') + { + *response_ptr = true; + reader.SetIsDone(true); + } + else if (bytes[0] == 'n' || bytes[0] == 'N') + { + *response_ptr = false; + reader.SetIsDone(true); + } + else + { + if (out_file.IsValid() && !reader.IsDone() && reader.GetPrompt()) + { + out_file.Printf ("Please answer \"y\" or \"n\".\n%s", reader.GetPrompt()); + out_file.Flush (); + } + } + break; + + case eInputReaderInterrupt: + case eInputReaderEndOfFile: + *response_ptr = false; // Assume ^C or ^D means cancel the proposed action + reader.SetIsDone (true); + break; + + case eInputReaderDone: + break; + } + + return bytes_len; + +} + +bool +CommandInterpreter::Confirm (const char *message, bool default_answer) +{ + // Check AutoConfirm first: + if (m_debugger.GetAutoConfirm()) + return default_answer; + + InputReaderSP reader_sp (new InputReader(GetDebugger())); + bool response = default_answer; + if (reader_sp) + { + std::string prompt(message); + prompt.append(": ["); + if (default_answer) + prompt.append ("Y/n] "); + else + prompt.append ("y/N] "); + + Error err (reader_sp->Initialize (CommandInterpreter::GetConfirmationInputReaderCallback, + &response, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + prompt.c_str(), // prompt + true)); // echo input + if (err.Success()) + { + GetDebugger().PushInputReader (reader_sp); + } + reader_sp->WaitOnReaderIsDone(); + } + return response; +} + +OptionArgVectorSP +CommandInterpreter::GetAliasOptions (const char *alias_name) +{ + OptionArgMap::iterator pos; + OptionArgVectorSP ret_val; + + std::string alias (alias_name); + + if (HasAliasOptions()) + { + pos = m_alias_options.find (alias); + if (pos != m_alias_options.end()) + ret_val = pos->second; + } + + return ret_val; +} + +void +CommandInterpreter::RemoveAliasOptions (const char *alias_name) +{ + OptionArgMap::iterator pos = m_alias_options.find(alias_name); + if (pos != m_alias_options.end()) + { + m_alias_options.erase (pos); + } +} + +void +CommandInterpreter::AddOrReplaceAliasOptions (const char *alias_name, OptionArgVectorSP &option_arg_vector_sp) +{ + m_alias_options[alias_name] = option_arg_vector_sp; +} + +bool +CommandInterpreter::HasCommands () +{ + return (!m_command_dict.empty()); +} + +bool +CommandInterpreter::HasAliases () +{ + return (!m_alias_dict.empty()); +} + +bool +CommandInterpreter::HasUserCommands () +{ + return (!m_user_dict.empty()); +} + +bool +CommandInterpreter::HasAliasOptions () +{ + return (!m_alias_options.empty()); +} + +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 = GetAliasOptions (alias_name); + + 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.compare (cmd_args.GetArgumentAtIndex(0)) != 0) + cmd_args.Unshift (alias_name); + + 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 used (old_size + 1, false); + + used[0] = true; + + for (size_t i = 0; i < option_arg_vector->size(); ++i) + { + OptionArgPair option_pair = (*option_arg_vector)[i]; + OptionArgValue value_pair = option_pair.second; + int value_type = value_pair.first; + std::string option = option_pair.first; + std::string value = value_pair.second; + if (option.compare ("") == 0) + { + if (!wants_raw_input + || (value.compare("--") != 0)) // Since we inserted this above, make sure we don't insert it twice + new_args.AppendArgument (value.c_str()); + } + else + { + if (value_type != optional_argument) + new_args.AppendArgument (option.c_str()); + if (value.compare ("") != 0) + { + int index = GetOptionArgumentPosition (value.c_str()); + if (index == 0) + { + // value was NOT a positional argument; must be a real value + if (value_type != optional_argument) + new_args.AppendArgument (value.c_str()); + else + { + char buffer[255]; + ::snprintf (buffer, sizeof (buffer), "%s%s", option.c_str(), value.c_str()); + new_args.AppendArgument (buffer); + } + + } + else if (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 != optional_argument) + 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 (size_t j = 0; j < cmd_args.GetArgumentCount(); ++j) + { + if (!used[j] && !wants_raw_input) + new_args.AppendArgument (cmd_args.GetArgumentAtIndex (j)); + } + + cmd_args.Clear(); + cmd_args.SetArguments (new_args.GetArgumentCount(), (const char **) new_args.GetArgumentVector()); + } + 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(), (const char **) new_args.GetArgumentVector()); + } + 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. + + char *cptr = (char *) 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; +} + +void +CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result) +{ + FileSpec init_file; + if (in_cwd) + { + // In the current working directory we don't load any program specific + // .lldbinit files, we only look for a "./.lldbinit" file. + if (m_skip_lldbinit_files) + return; + + init_file.SetFile ("./.lldbinit", true); + } + else + { + // If we aren't looking in the current working directory we are looking + // in the home directory. 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. We also obey any requests to not + // load the init files. + const char *init_file_path = "~/.lldbinit"; + + if (m_skip_app_init_files == false) + { + FileSpec program_file_spec (Host::GetProgramFileSpec()); + const char *program_name = program_file_spec.GetFilename().AsCString(); + + if (program_name) + { + char program_init_file_name[PATH_MAX]; + ::snprintf (program_init_file_name, sizeof(program_init_file_name), "%s-%s", init_file_path, program_name); + init_file.SetFile (program_init_file_name, true); + if (!init_file.Exists()) + init_file.Clear(); + } + } + + if (!init_file && !m_skip_lldbinit_files) + init_file.SetFile (init_file_path, true); + } + + // If the file exists, tell HandleCommand to 'source' it; this will do the actual broadcasting + // of the commands back to any appropriate listener (see CommandObjectSource::Execute for more details). + + if (init_file.Exists()) + { + ExecutionContext *exe_ctx = NULL; // We don't have any context yet. + bool stop_on_continue = true; + bool stop_on_error = false; + bool echo_commands = false; + bool print_results = false; + + HandleCommandsFromFile (init_file, exe_ctx, stop_on_continue, stop_on_error, echo_commands, print_results, eLazyBoolNo, result); + } + else + { + // nothing to be done if the file doesn't exist + result.SetStatus(eReturnStatusSuccessFinishNoResult); + } +} + +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, + bool stop_on_continue, + bool stop_on_error, + bool echo_commands, + bool print_results, + LazyBool add_to_history, + 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 != NULL) + UpdateExecutionContext (override_context); + + if (!stop_on_continue) + { + m_debugger.SetAsyncExecution (false); + } + + for (size_t idx = 0; idx < num_lines; idx++) + { + const char *cmd = commands.GetStringAtIndex(idx); + if (cmd[0] == '\0') + continue; + + if (echo_commands) + { + result.AppendMessageWithFormat ("%s %s\n", + GetPrompt(), + 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 (!add_to_history) + m_command_source_depth++; + bool success = HandleCommand(cmd, add_to_history, tmp_result, + NULL, /* override_context */ + true, /* repeat_on_empty_command */ + override_context != NULL /* no_context_switching */); + if (!add_to_history) + m_command_source_depth--; + + if (print_results) + { + if (tmp_result.Succeeded()) + result.AppendMessageWithFormat("%s", tmp_result.GetOutputData()); + } + + if (!success || !tmp_result.Succeeded()) + { + const char *error_msg = tmp_result.GetErrorData(); + if (error_msg == NULL || error_msg[0] == '\0') + error_msg = ".\n"; + if (stop_on_error) + { + result.AppendErrorWithFormat("Aborting reading of commands after command #%zu: '%s' failed with %s", + idx, cmd, error_msg); + result.SetStatus (eReturnStatusFailed); + m_debugger.SetAsyncExecution (old_async_execution); + return; + } + else if (print_results) + { + result.AppendMessageWithFormat ("Command #%zu '%s' failed with %s", + idx + 1, + cmd, + error_msg); + } + } + + 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 (stop_on_continue) + { + // 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 #%zu: '%s' continued the target.\n", + idx + 1, cmd); + else + result.AppendMessageWithFormat ("Command #%zu '%s' continued the target.\n", 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; +} + +void +CommandInterpreter::HandleCommandsFromFile (FileSpec &cmd_file, + ExecutionContext *context, + bool stop_on_continue, + bool stop_on_error, + bool echo_command, + bool print_result, + LazyBool add_to_history, + CommandReturnObject &result) +{ + if (cmd_file.Exists()) + { + bool success; + StringList commands; + success = commands.ReadFileLines(cmd_file); + if (!success) + { + result.AppendErrorWithFormat ("Error reading commands from file: %s.\n", cmd_file.GetFilename().AsCString()); + result.SetStatus (eReturnStatusFailed); + return; + } + m_command_source_depth++; + HandleCommands (commands, context, stop_on_continue, stop_on_error, echo_command, print_result, add_to_history, result); + m_command_source_depth--; + } + else + { + result.AppendErrorWithFormat ("Error reading commands from file %s - file not found.\n", + cmd_file.GetFilename().AsCString()); + result.SetStatus (eReturnStatusFailed); + return; + } +} + +ScriptInterpreter * +CommandInterpreter::GetScriptInterpreter (bool can_create) +{ + if (m_script_interpreter_ap.get() != NULL) + return m_script_interpreter_ap.get(); + + if (!can_create) + return NULL; + + // + // we need to protect the initialization of the script interpreter + // otherwise we could end up with two threads both trying to create + // their instance of it, and for some languages (e.g. Python) + // this is a bulletproof recipe for disaster! + // this needs to be a function-level static because multiple Debugger instances living in the same process + // still need to be isolated and not try to initialize Python concurrently + static Mutex g_interpreter_mutex(Mutex::eMutexTypeRecursive); + Mutex::Locker interpreter_lock(g_interpreter_mutex); + + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT)); + if (log) + log->Printf("Initializing the ScriptInterpreter now\n"); + + lldb::ScriptLanguage script_lang = GetDebugger().GetScriptLanguage(); + switch (script_lang) + { + case eScriptLanguagePython: +#ifndef LLDB_DISABLE_PYTHON + m_script_interpreter_ap.reset (new ScriptInterpreterPython (*this)); + break; +#else + // Fall through to the None case when python is disabled +#endif + case eScriptLanguageNone: + m_script_interpreter_ap.reset (new ScriptInterpreterNone (*this)); + break; + }; + + return m_script_interpreter_ap.get(); +} + + + +bool +CommandInterpreter::GetSynchronous () +{ + return m_synchronous_execution; +} + +void +CommandInterpreter::SetSynchronous (bool value) +{ + m_synchronous_execution = value; +} + +void +CommandInterpreter::OutputFormattedHelpText (Stream &strm, + const char *word_text, + const char *separator, + const char *help_text, + size_t max_word_len) +{ + const uint32_t max_columns = m_debugger.GetTerminalWidth(); + + int indent_size = max_word_len + strlen (separator) + 2; + + strm.IndentMore (indent_size); + + StreamString text_strm; + text_strm.Printf ("%-*s %s %s", (int)max_word_len, word_text, separator, help_text); + + size_t len = text_strm.GetSize(); + const char *text = text_strm.GetData(); + if (text[len - 1] == '\n') + { + text_strm.EOL(); + len = text_strm.GetSize(); + } + + if (len < max_columns) + { + // Output it as a single line. + strm.Printf ("%s", text); + } + else + { + // We need to break it up into multiple lines. + bool first_line = true; + int text_width; + size_t start = 0; + size_t end = start; + const size_t final_end = strlen (text); + + while (end < final_end) + { + if (first_line) + text_width = max_columns - 1; + else + text_width = max_columns - indent_size - 1; + + // Don't start the 'text' on a space, since we're already outputting the indentation. + if (!first_line) + { + while ((start < final_end) && (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 + && text[end] != ' ' && text[end] != '\t' && text[end] != '\n') + end--; + assert (end > 0); + } + + const size_t sub_len = end - start; + if (start != 0) + strm.EOL(); + if (!first_line) + strm.Indent(); + else + first_line = false; + assert (start <= final_end); + assert (start + sub_len <= final_end); + if (sub_len > 0) + strm.Write (text + start, sub_len); + start = end + 1; + } + } + strm.EOL(); + strm.IndentLess(indent_size); +} + +void +CommandInterpreter::OutputHelpText (Stream &strm, + const char *word_text, + const char *separator, + const char *help_text, + uint32_t max_word_len) +{ + int indent_size = max_word_len + strlen (separator) + 2; + + strm.IndentMore (indent_size); + + StreamString text_strm; + text_strm.Printf ("%-*s %s %s", max_word_len, word_text, separator, help_text); + + const uint32_t max_columns = m_debugger.GetTerminalWidth(); + + size_t len = text_strm.GetSize(); + const char *text = text_strm.GetData(); + + uint32_t chars_left = max_columns; + + for (uint32_t i = 0; i < len; i++) + { + if ((text[i] == ' ' && ::strchr((text+i+1), ' ') && chars_left < ::strchr((text+i+1), ' ')-(text+i)) || text[i] == '\n') + { + chars_left = max_columns - indent_size; + strm.EOL(); + strm.Indent(); + } + else + { + strm.PutChar(text[i]); + chars_left--; + } + + } + + strm.EOL(); + strm.IndentLess(indent_size); +} + +void +CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList &commands_found, + StringList &commands_help, bool search_builtin_commands, bool search_user_commands) +{ + CommandObject::CommandMap::const_iterator pos; + + if (search_builtin_commands) + { + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + { + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); + + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + cmd_obj->AproposAllSubCommands (command_name, + search_word, + commands_found, + commands_help); + + } + } + + if (search_user_commands) + { + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) + { + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); + + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + cmd_obj->AproposAllSubCommands (command_name, + search_word, + commands_found, + commands_help); + + } + } +} + + +void +CommandInterpreter::UpdateExecutionContext (ExecutionContext *override_context) +{ + if (override_context != NULL) + { + m_exe_ctx_ref = *override_context; + } + else + { + const bool adopt_selected = true; + m_exe_ctx_ref.SetTargetPtr (m_debugger.GetSelectedTarget().get(), adopt_selected); + } +} diff --git a/source/Interpreter/CommandObject.cpp b/source/Interpreter/CommandObject.cpp new file mode 100644 index 00000000000..291dc401357 --- /dev/null +++ b/source/Interpreter/CommandObject.cpp @@ -0,0 +1,1174 @@ +//===-- CommandObject.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/CommandObject.h" + +#include +#include + +#include +#include +#include + +#include "lldb/Core/Address.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/Options.h" + +// These are for the Sourcename completers. +// FIXME: Make a separate file for the completers. +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObject +//------------------------------------------------------------------------- + +CommandObject::CommandObject +( + CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t flags +) : + m_interpreter (interpreter), + m_cmd_name (name), + m_cmd_help_short (), + m_cmd_help_long (), + m_cmd_syntax (), + m_is_alias (false), + m_flags (flags), + m_arguments(), + m_command_override_callback (NULL), + m_command_override_baton (NULL) +{ + if (help && help[0]) + m_cmd_help_short = help; + if (syntax && syntax[0]) + m_cmd_syntax = syntax; +} + +CommandObject::~CommandObject () +{ +} + +const char * +CommandObject::GetHelp () +{ + return m_cmd_help_short.c_str(); +} + +const char * +CommandObject::GetHelpLong () +{ + return m_cmd_help_long.c_str(); +} + +const char * +CommandObject::GetSyntax () +{ + if (m_cmd_syntax.length() == 0) + { + StreamString syntax_str; + syntax_str.Printf ("%s", GetCommandName()); + if (GetOptions() != NULL) + syntax_str.Printf (" "); + if (m_arguments.size() > 0) + { + syntax_str.Printf (" "); + if (WantsRawCommandString() && GetOptions() && GetOptions()->NumCommandOptions()) + syntax_str.Printf("-- "); + GetFormattedCommandArguments (syntax_str); + } + m_cmd_syntax = syntax_str.GetData (); + } + + return m_cmd_syntax.c_str(); +} + +const char * +CommandObject::GetCommandName () +{ + return m_cmd_name.c_str(); +} + +void +CommandObject::SetCommandName (const char *name) +{ + m_cmd_name = name; +} + +void +CommandObject::SetHelp (const char *cstr) +{ + m_cmd_help_short = cstr; +} + +void +CommandObject::SetHelpLong (const char *cstr) +{ + m_cmd_help_long = cstr; +} + +void +CommandObject::SetHelpLong (std::string str) +{ + m_cmd_help_long = str; +} + +void +CommandObject::SetSyntax (const char *cstr) +{ + m_cmd_syntax = cstr; +} + +Options * +CommandObject::GetOptions () +{ + // By default commands don't have options unless this virtual function + // is overridden by base classes. + return NULL; +} + +bool +CommandObject::ParseOptions +( + Args& args, + CommandReturnObject &result +) +{ + // See if the subclass has options? + Options *options = GetOptions(); + if (options != NULL) + { + Error error; + options->NotifyOptionParsingStarting(); + + // ParseOptions calls getopt_long_only, which always skips the zero'th item in the array and starts at position 1, + // so we need to push a dummy value into position zero. + args.Unshift("dummy_string"); + error = args.ParseOptions (*options); + + // The "dummy_string" will have already been removed by ParseOptions, + // so no need to remove it. + + if (error.Success()) + error = options->NotifyOptionParsingFinished(); + + 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); + } + } + result.SetStatus (eReturnStatusFailed); + return false; + } + return true; +} + + + +bool +CommandObject::CheckRequirements (CommandReturnObject &result) +{ +#ifdef LLDB_CONFIGURATION_DEBUG + // 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() == NULL); + assert (m_exe_ctx.GetProcessPtr() == NULL); + assert (m_exe_ctx.GetThreadPtr() == NULL); + assert (m_exe_ctx.GetFramePtr() == NULL); +#endif + + // 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 & (eFlagRequiresTarget | + eFlagRequiresProcess | + eFlagRequiresThread | + eFlagRequiresFrame | + eFlagTryTargetAPILock )) + { + + if ((flags & eFlagRequiresTarget) && !m_exe_ctx.HasTargetScope()) + { + result.AppendError (GetInvalidTargetDescription()); + return false; + } + + if ((flags & eFlagRequiresProcess) && !m_exe_ctx.HasProcessScope()) + { + result.AppendError (GetInvalidProcessDescription()); + return false; + } + + if ((flags & eFlagRequiresThread) && !m_exe_ctx.HasThreadScope()) + { + result.AppendError (GetInvalidThreadDescription()); + return false; + } + + if ((flags & eFlagRequiresFrame) && !m_exe_ctx.HasFrameScope()) + { + result.AppendError (GetInvalidFrameDescription()); + return false; + } + + if ((flags & eFlagRequiresRegContext) && (m_exe_ctx.GetRegisterContext() == NULL)) + { + result.AppendError (GetInvalidRegContextDescription()); + return false; + } + + if (flags & eFlagTryTargetAPILock) + { + Target *target = m_exe_ctx.GetTargetPtr(); + if (target) + { + if (m_api_locker.TryLock (target->GetAPIMutex(), NULL) == false) + { + result.AppendError ("failed to get API lock"); + return false; + } + } + } + } + + if (GetFlags().AnySet (CommandObject::eFlagProcessMustBeLaunched | CommandObject::eFlagProcessMustBePaused)) + { + Process *process = m_interpreter.GetExecutionContext().GetProcessPtr(); + if (process == NULL) + { + // A process that is not running is considered paused. + if (GetFlags().Test(CommandObject::eFlagProcessMustBeLaunched)) + { + 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(CommandObject::eFlagProcessMustBeLaunched)) + { + result.AppendError ("Process must be launched."); + result.SetStatus (eReturnStatusFailed); + return false; + } + break; + + case eStateRunning: + case eStateStepping: + if (GetFlags().Test(CommandObject::eFlagProcessMustBePaused)) + { + 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(); + m_api_locker.Unlock(); +} + + +class CommandDictCommandPartialMatch +{ + public: + CommandDictCommandPartialMatch (const char *match_str) + { + m_match_str = match_str; + } + bool operator() (const std::pair map_element) const + { + // A NULL or empty string matches everything. + if (m_match_str == NULL || *m_match_str == '\0') + return true; + + return map_element.first.find (m_match_str, 0) == 0; + } + + private: + const char *m_match_str; +}; + +int +CommandObject::AddNamesMatchingPartialString (CommandObject::CommandMap &in_map, const char *cmd_str, + StringList &matches) +{ + int number_added = 0; + CommandDictCommandPartialMatch matcher(cmd_str); + + CommandObject::CommandMap::iterator matching_cmds = std::find_if (in_map.begin(), in_map.end(), matcher); + + while (matching_cmds != in_map.end()) + { + ++number_added; + matches.AppendString((*matching_cmds).first.c_str()); + matching_cmds = std::find_if (++matching_cmds, in_map.end(), matcher);; + } + return number_added; +} + +int +CommandObject::HandleCompletion +( + Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches +) +{ + // Default implmentation 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. + matches.Clear(); + return -1; + } + else + { + // Can we do anything generic with the options? + Options *cur_options = GetOptions(); + CommandReturnObject result; + OptionElementVector opt_element_vector; + + if (cur_options != NULL) + { + // Re-insert the dummy command name string which will have been + // stripped off: + input.Unshift ("dummy-string"); + cursor_index++; + + + // I stick an element on the end of the input, because if the last element is + // option that requires an argument, getopt_long_only will freak out. + + input.AppendArgument (""); + + input.ParseArgsForCompletion (*cur_options, opt_element_vector, cursor_index); + + input.DeleteArgumentAtIndex(input.GetArgumentCount() - 1); + + bool handled_by_options; + handled_by_options = cur_options->HandleOptionCompletion (input, + opt_element_vector, + cursor_index, + cursor_char_position, + match_start_point, + max_return_elements, + word_complete, + matches); + if (handled_by_options) + return matches.GetSize(); + } + + // If we got here, the last word is not an option or an option argument. + return HandleArgumentCompletion (input, + cursor_index, + cursor_char_position, + opt_element_vector, + match_start_point, + max_return_elements, + word_complete, + matches); + } +} + +bool +CommandObject::HelpTextContainsWord (const char *search_word) +{ + std::string options_usage_help; + + bool found_word = false; + + const char *short_help = GetHelp(); + const char *long_help = GetHelpLong(); + const char *syntax_help = GetSyntax(); + + if (short_help && strcasestr (short_help, search_word)) + found_word = true; + else if (long_help && strcasestr (long_help, search_word)) + found_word = true; + else if (syntax_help && strcasestr (syntax_help, search_word)) + found_word = true; + + if (!found_word + && GetOptions() != NULL) + { + StreamString usage_help; + GetOptions()->GenerateOptionUsage (usage_help, this); + if (usage_help.GetSize() > 0) + { + const char *usage_text = usage_help.GetData(); + if (strcasestr (usage_text, search_word)) + found_word = true; + } + } + + return found_word; +} + +int +CommandObject::GetNumArgumentEntries () +{ + return m_arguments.size(); +} + +CommandObject::CommandArgumentEntry * +CommandObject::GetArgumentEntryAtIndex (int idx) +{ + if (idx < m_arguments.size()) + return &(m_arguments[idx]); + + return NULL; +} + +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 (ArgumentTableEntry *) &(table[i]); + + return NULL; +} + +void +CommandObject::GetArgumentHelp (Stream &str, CommandArgumentType arg_type, CommandInterpreter &interpreter) +{ + const ArgumentTableEntry* table = CommandObject::GetArgumentTable(); + ArgumentTableEntry *entry = (ArgumentTableEntry *) &(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) + { + const char* help_text = entry->help_function(); + if (!entry->help_function.self_formatting) + { + interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", help_text, + name_str.GetSize()); + } + else + { + interpreter.OutputHelpText(str, name_str.GetData(), "--", help_text, + name_str.GetSize()); + } + } + else + interpreter.OutputFormattedHelpText (str, name_str.GetData(), "--", entry->help_text, name_str.GetSize()); +} + +const char * +CommandObject::GetArgumentName (CommandArgumentType arg_type) +{ + ArgumentTableEntry *entry = (ArgumentTableEntry *) &(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; + + StreamString str; + str << "Arg name for type (" << arg_type << ") not in arg table!"; + return str.GetData(); +} + +bool +CommandObject::IsPairType (ArgumentRepetitionType arg_repeat_type) +{ + if ((arg_repeat_type == eArgRepeatPairPlain) + || (arg_repeat_type == eArgRepeatPairOptional) + || (arg_repeat_type == eArgRepeatPairPlus) + || (arg_repeat_type == eArgRepeatPairStar) + || (arg_repeat_type == eArgRepeatPairRange) + || (arg_repeat_type == eArgRepeatPairRangeOptional)) + return true; + + return false; +} + +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)); + } + switch (arg_entry[0].arg_repetition) + { + case eArgRepeatPlain: + str.Printf ("<%s>", names.GetData()); + break; + case eArgRepeatPlus: + str.Printf ("<%s> [<%s> [...]]", names.GetData(), names.GetData()); + break; + case eArgRepeatStar: + str.Printf ("[<%s> [<%s> [...]]]", names.GetData(), names.GetData()); + break; + case eArgRepeatOptional: + str.Printf ("[<%s>]", names.GetData()); + break; + case eArgRepeatRange: + str.Printf ("<%s_1> .. <%s_n>", names.GetData(), names.GetData()); + 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 (const char *arg_name) +{ + CommandArgumentType return_type = eArgTypeLastArg; + + std::string arg_name_str (arg_name); + size_t len = arg_name_str.length(); + if (arg_name[0] == '<' + && arg_name[len-1] == '>') + arg_name_str = arg_name_str.substr (1, len-2); + + const ArgumentTableEntry *table = GetArgumentTable(); + for (int i = 0; i < eArgTypeLastArg; ++i) + if (arg_name_str.compare (table[i].arg_name) == 0) + return_type = g_arguments_data[i].arg_type; + + return return_type; +} + +static const char * +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 const char * +BreakpointIDHelpTextCallback () +{ + return "Breakpoint ID's consist 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 id number, " + "or the major number, a dot, and the location number (e.g. 3 or 3.2 could " + "both be valid breakpoint ids)."; +} + +static const char * +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 " + " - . 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 const char * +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 const char * +FormatHelpTextCallback () +{ + + static char* help_text_ptr = NULL; + + if (help_text_ptr) + return help_text_ptr; + + 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(); + + std::string data = sstr.GetString(); + + help_text_ptr = new char[data.length()+1]; + + data.copy(help_text_ptr, data.length()); + + return help_text_ptr; +} + +static const char * +LanguageTypeHelpTextCallback () +{ + static char* help_text_ptr = NULL; + + if (help_text_ptr) + return help_text_ptr; + + StreamString sstr; + sstr << "One of the following languages:\n"; + + for (unsigned int l = eLanguageTypeUnknown; l < eNumLanguageTypes; ++l) + { + sstr << " " << LanguageRuntime::GetNameForLanguageType(static_cast(l)) << "\n"; + } + + sstr.Flush(); + + std::string data = sstr.GetString(); + + help_text_ptr = new char[data.length()+1]; + + data.copy(help_text_ptr, data.length()); + + return help_text_ptr; +} + +static const char * +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 const char * +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::GenerateHelpText (CommandReturnObject &result) +{ + GenerateHelpText(result.GetOutputStream()); + + result.SetStatus (eReturnStatusSuccessFinishNoResult); +} + +void +CommandObject::GenerateHelpText (Stream &output_strm) +{ + CommandInterpreter& interpreter = GetCommandInterpreter(); + if (GetOptions() != NULL) + { + if (WantsRawCommandString()) + { + std::string help_text (GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1); + } + else + interpreter.OutputFormattedHelpText (output_strm, "", "", GetHelp(), 1); + output_strm.Printf ("\nSyntax: %s\n", GetSyntax()); + GetOptions()->GenerateOptionUsage (output_strm, this); + const char *long_help = GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("\n%s", long_help); + 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 + && GetOptions() + && GetOptions()->NumCommandOptions() > 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); + } + } + else if (IsMultiwordObject()) + { + if (WantsRawCommandString()) + { + std::string help_text (GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1); + } + else + interpreter.OutputFormattedHelpText (output_strm, "", "", GetHelp(), 1); + GenerateHelpText (output_strm); + } + else + { + const char *long_help = GetHelpLong(); + if ((long_help != NULL) + && (strlen (long_help) > 0)) + output_strm.Printf ("%s", long_help); + else if (WantsRawCommandString()) + { + std::string help_text (GetHelp()); + help_text.append (" This command takes 'raw' input (no need to quote stuff)."); + interpreter.OutputFormattedHelpText (output_strm, "", "", help_text.c_str(), 1); + } + else + interpreter.OutputFormattedHelpText (output_strm, "", "", GetHelp(), 1); + output_strm.Printf ("\nSyntax: %s\n", GetSyntax()); + } +} + +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) +{ + if (arg_type >=0 && arg_type < eArgTypeLastArg) + return g_arguments_data[arg_type].arg_name; + return NULL; + +} + +const char * +CommandObject::GetArgumentDescriptionAsCString (const lldb::CommandArgumentType arg_type) +{ + if (arg_type >=0 && arg_type < eArgTypeLastArg) + return g_arguments_data[arg_type].help_text; + return NULL; +} + +bool +CommandObjectParsed::Execute (const char *args_string, CommandReturnObject &result) +{ + CommandOverrideCallback command_callback = GetOverrideCallback(); + bool handled = false; + Args cmd_args (args_string); + if (command_callback) + { + Args full_args (GetCommandName ()); + full_args.AppendArguments(cmd_args); + handled = command_callback (GetOverrideCallbackBaton(), full_args.GetConstArgumentVector()); + } + if (!handled) + { + for (size_t i = 0; i < cmd_args.GetArgumentCount(); ++i) + { + const char *tmp_str = cmd_args.GetArgumentAtIndex (i); + if (tmp_str[0] == '`') // back-quote + cmd_args.ReplaceArgumentAtIndex (i, m_interpreter.ProcessEmbeddedScriptCommands (tmp_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) +{ + CommandOverrideCallback command_callback = GetOverrideCallback(); + bool handled = false; + if (command_callback) + { + std::string full_command (GetCommandName ()); + full_command += ' '; + full_command += args_string; + const char *argv[2] = { NULL, NULL }; + argv[0] = full_command.c_str(); + handled = command_callback (GetOverrideCallbackBaton(), argv); + } + if (!handled) + { + if (CheckRequirements(result)) + handled = DoExecute (args_string, result); + + Cleanup(); + } + return handled; +} + +static +const char *arch_helper() +{ + static StreamString g_archs_help; + if (g_archs_help.Empty()) + { + StringList archs; + ArchSpec::AutoComplete(NULL, archs); + g_archs_help.Printf("These are the supported architecture names:\n"); + archs.Join("\n", g_archs_help); + } + return g_archs_help.GetData(); +} + +CommandObject::ArgumentTableEntry +CommandObject::g_arguments_data[] = +{ + { eArgTypeAddress, "address", CommandCompletions::eNoCompletion, { NULL, false }, "A valid address in the target program's execution space." }, + { eArgTypeAddressOrExpression, "address-expression", CommandCompletions::eNoCompletion, { NULL, false }, "An expression that resolves to an address." }, + { eArgTypeAliasName, "alias-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of an abbreviation (alias) for a debugger command." }, + { eArgTypeAliasOptions, "options-for-aliased-command", CommandCompletions::eNoCompletion, { NULL, 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, { NULL, false }, "A Boolean value: 'true' or 'false'" }, + { eArgTypeBreakpointID, "breakpt-id", CommandCompletions::eNoCompletion, { BreakpointIDHelpTextCallback, false }, NULL }, + { eArgTypeBreakpointIDRange, "breakpt-id-list", CommandCompletions::eNoCompletion, { BreakpointIDRangeHelpTextCallback, false }, NULL }, + { eArgTypeByteSize, "byte-size", CommandCompletions::eNoCompletion, { NULL, false }, "Number of bytes to use." }, + { eArgTypeClassName, "class-name", CommandCompletions::eNoCompletion, { NULL, false }, "Then name of a class from the debug information in the program." }, + { eArgTypeCommandName, "cmd-name", CommandCompletions::eNoCompletion, { NULL, false }, "A debugger command (may be multiple words), without any options or arguments." }, + { eArgTypeCount, "count", CommandCompletions::eNoCompletion, { NULL, false }, "An unsigned integer." }, + { eArgTypeDirectoryName, "directory", CommandCompletions::eDiskDirectoryCompletion, { NULL, false }, "A directory name." }, + { eArgTypeDisassemblyFlavor, "disassembly-flavor", CommandCompletions::eNoCompletion, { NULL, false }, "A disassembly flavor recognized by your disassembly plugin. Currently the only valid options are \"att\" and \"intel\" for Intel targets" }, + { eArgTypeEndAddress, "end-address", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeExpressionPath, "expr-path", CommandCompletions::eNoCompletion, { ExprPathHelpTextCallback, true }, NULL }, + { eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, { NULL, false }, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" }, + { eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, { NULL, false }, "The name of a file (can include path)." }, + { eArgTypeFormat, "format", CommandCompletions::eNoCompletion, { FormatHelpTextCallback, true }, NULL }, + { eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, { NULL, false }, "Index into a thread's list of frames." }, + { eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a function." }, + { eArgTypeFunctionOrSymbol, "function-or-symbol", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a function or symbol." }, + { eArgTypeGDBFormat, "gdb-format", CommandCompletions::eNoCompletion, { GDBFormatHelpTextCallback, true }, NULL }, + { eArgTypeIndex, "index", CommandCompletions::eNoCompletion, { NULL, false }, "An index into a list." }, + { eArgTypeLanguage, "language", CommandCompletions::eNoCompletion, { LanguageTypeHelpTextCallback, true }, NULL }, + { eArgTypeLineNum, "linenum", CommandCompletions::eNoCompletion, { NULL, false }, "Line number in a source file." }, + { eArgTypeLogCategory, "log-category", CommandCompletions::eNoCompletion, { NULL, 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, { NULL, 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, { NULL, false }, "A C++ method name." }, + { eArgTypeName, "name", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeNewPathPrefix, "new-path-prefix", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeNumLines, "num-lines", CommandCompletions::eNoCompletion, { NULL, false }, "The number of lines to use." }, + { eArgTypeNumberPerLine, "number-per-line", CommandCompletions::eNoCompletion, { NULL, false }, "The number of items per line to display." }, + { eArgTypeOffset, "offset", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeOldPathPrefix, "old-path-prefix", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeOneLiner, "one-line-command", CommandCompletions::eNoCompletion, { NULL, false }, "A command that is entered as a single line of text." }, + { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { NULL, false }, "The process ID number." }, + { eArgTypePlugin, "plugin", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of the process." }, + { eArgTypePythonClass, "python-class", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a Python class." }, + { eArgTypePythonFunction, "python-function", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a Python function." }, + { eArgTypePythonScript, "python-script", CommandCompletions::eNoCompletion, { NULL, false }, "Source code written in Python." }, + { eArgTypeQueueName, "queue-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of the thread queue." }, + { eArgTypeRegisterName, "register-name", CommandCompletions::eNoCompletion, { RegisterNameHelpTextCallback, true }, NULL }, + { eArgTypeRegularExpression, "regular-expression", CommandCompletions::eNoCompletion, { NULL, false }, "A regular expression." }, + { eArgTypeRunArgs, "run-args", CommandCompletions::eNoCompletion, { NULL, false }, "Arguments to be passed to the target program when it starts executing." }, + { eArgTypeRunMode, "run-mode", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeScriptedCommandSynchronicity, "script-cmd-synchronicity", CommandCompletions::eNoCompletion, { NULL, false }, "The synchronicity to use to run scripted commands with regard to LLDB event system." }, + { eArgTypeScriptLang, "script-language", CommandCompletions::eNoCompletion, { NULL, false }, "The scripting language to be used for script-based commands. Currently only Python is valid." }, + { eArgTypeSearchWord, "search-word", CommandCompletions::eNoCompletion, { NULL, false }, "The word for which you wish to search for information about." }, + { eArgTypeSelector, "selector", CommandCompletions::eNoCompletion, { NULL, false }, "An Objective-C selector name." }, + { eArgTypeSettingIndex, "setting-index", CommandCompletions::eNoCompletion, { NULL, 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, { NULL, 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, { NULL, false }, "The name of a settable internal debugger variable up to a dot ('.'), e.g. 'target.process.'" }, + { eArgTypeSettingVariableName, "setting-variable-name", CommandCompletions::eNoCompletion, { NULL, 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, { NULL, false }, "The name of a shared library." }, + { eArgTypeSourceFile, "source-file", CommandCompletions::eSourceFileCompletion, { NULL, false }, "The name of a source file.." }, + { eArgTypeSortOrder, "sort-order", CommandCompletions::eNoCompletion, { NULL, false }, "Specify a sort order when dumping lists." }, + { eArgTypeStartAddress, "start-address", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeSummaryString, "summary-string", CommandCompletions::eNoCompletion, { SummaryStringHelpTextCallback, true }, NULL }, + { eArgTypeSymbol, "symbol", CommandCompletions::eSymbolCompletion, { NULL, false }, "Any symbol name (function name, variable, argument, etc.)" }, + { eArgTypeThreadID, "thread-id", CommandCompletions::eNoCompletion, { NULL, false }, "Thread ID number." }, + { eArgTypeThreadIndex, "thread-index", CommandCompletions::eNoCompletion, { NULL, false }, "Index into the process' list of threads." }, + { eArgTypeThreadName, "thread-name", CommandCompletions::eNoCompletion, { NULL, false }, "The thread's name." }, + { eArgTypeUnsignedInteger, "unsigned-integer", CommandCompletions::eNoCompletion, { NULL, false }, "An unsigned integer." }, + { eArgTypeUnixSignal, "unix-signal", CommandCompletions::eNoCompletion, { NULL, false }, "A valid Unix signal name or number (e.g. SIGKILL, KILL or 9)." }, + { eArgTypeVarName, "variable-name", CommandCompletions::eNoCompletion, { NULL, false }, "The name of a variable in your program." }, + { eArgTypeValue, "value", CommandCompletions::eNoCompletion, { NULL, false }, "A value could be anything, depending on where and how it is used." }, + { eArgTypeWidth, "width", CommandCompletions::eNoCompletion, { NULL, false }, "Help text goes here." }, + { eArgTypeNone, "none", CommandCompletions::eNoCompletion, { NULL, false }, "No help available for this." }, + { eArgTypePlatform, "platform-name", CommandCompletions::ePlatformPluginCompletion, { NULL, 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, { NULL, false }, "Watchpoint IDs are positive integers." }, + { eArgTypeWatchpointIDRange, "watchpt-id-list", CommandCompletions::eNoCompletion, { NULL, false }, "For example, '1-3' or '1 to 3'." }, + { eArgTypeWatchType, "watch-type", CommandCompletions::eNoCompletion, { NULL, false }, "Specify the type for a watchpoint." } +}; + +const CommandObject::ArgumentTableEntry* +CommandObject::GetArgumentTable () +{ + // If this assertion fires, then the table above is out of date with the CommandArgumentType enumeration + assert ((sizeof (CommandObject::g_arguments_data) / sizeof (CommandObject::ArgumentTableEntry)) == eArgTypeLastArg); + return CommandObject::g_arguments_data; +} + + diff --git a/source/Interpreter/CommandObjectRegexCommand.cpp b/source/Interpreter/CommandObjectRegexCommand.cpp new file mode 100644 index 00000000000..59cf1f05fb6 --- /dev/null +++ b/source/Interpreter/CommandObjectRegexCommand.cpp @@ -0,0 +1,150 @@ +//===-- CommandObjectRegexCommand.cpp ---------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/CommandObjectRegexCommand.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +//---------------------------------------------------------------------- +// CommandObjectRegexCommand constructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::CommandObjectRegexCommand +( + CommandInterpreter &interpreter, + const char *name, + const char *help, + const char *syntax, + uint32_t max_matches, + uint32_t completion_type_mask +) : + CommandObjectRaw (interpreter, name, help, syntax), + m_max_matches (max_matches), + m_completion_type_mask (completion_type_mask), + m_entries () +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectRegexCommand::~CommandObjectRegexCommand() +{ +} + + +bool +CommandObjectRegexCommand::DoExecute +( + const char *command, + CommandReturnObject &result +) +{ + if (command) + { + 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, ®ex_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, NULL, true, true); + } + } + result.SetStatus(eReturnStatusFailed); + if (GetSyntax() != NULL) + result.AppendError (GetSyntax()); + else + result.AppendErrorWithFormat ("Command contents '%s' failed to match any regular expression in the '%s' regex command.\n", + command, + m_cmd_name.c_str()); + return false; + } + result.AppendError("empty command passed to regular expression command"); + result.SetStatus(eReturnStatusFailed); + 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 (re_cstr, REG_EXTENDED)) + { + m_entries.back().command.assign (command_cstr); + return true; + } + // The regex didn't compile... + m_entries.pop_back(); + return false; +} + +int +CommandObjectRegexCommand::HandleCompletion (Args &input, + int &cursor_index, + int &cursor_char_position, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + if (m_completion_type_mask) + { + std::string completion_str (input.GetArgumentAtIndex (cursor_index), cursor_char_position); + CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + m_completion_type_mask, + completion_str.c_str(), + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); + } + else + { + matches.Clear(); + word_complete = false; + } + return 0; +} diff --git a/source/Interpreter/CommandObjectScript.cpp b/source/Interpreter/CommandObjectScript.cpp new file mode 100644 index 00000000000..aff507d9858 --- /dev/null +++ b/source/Interpreter/CommandObjectScript.cpp @@ -0,0 +1,93 @@ +//===-- CommandObjectScript.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "CommandObjectScript.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +#include "lldb/Core/Debugger.h" + +#include "lldb/DataFormatters/DataVisualization.h" + +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// CommandObjectScript +//------------------------------------------------------------------------- + +CommandObjectScript::CommandObjectScript (CommandInterpreter &interpreter, ScriptLanguage script_lang) : + CommandObjectRaw (interpreter, + "script", + "Pass an expression to the script interpreter for evaluation and return the results. Drop into the interactive interpreter if no expression is given.", + "script []") +{ +} + +CommandObjectScript::~CommandObjectScript () +{ +} + +bool +CommandObjectScript::DoExecute +( + const char *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 = m_interpreter.GetScriptInterpreter (); + + if (script_interpreter == NULL) + { + 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 == NULL || command[0] == '\0') + { + 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/source/Interpreter/CommandObjectScript.h b/source/Interpreter/CommandObjectScript.h new file mode 100644 index 00000000000..fd55fc44a46 --- /dev/null +++ b/source/Interpreter/CommandObjectScript.h @@ -0,0 +1,42 @@ +//===-- CommandObjectScript.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_CommandObjectScript_h_ +#define liblldb_CommandObjectScript_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" + +namespace lldb_private { + +//------------------------------------------------------------------------- +// CommandObjectScript +//------------------------------------------------------------------------- + +class CommandObjectScript : public CommandObjectRaw +{ +public: + + CommandObjectScript (CommandInterpreter &interpreter, + lldb::ScriptLanguage script_lang); + + virtual + ~CommandObjectScript (); + +protected: + virtual bool + DoExecute (const char *command, CommandReturnObject &result); +}; + +} // namespace lldb_private + +#endif // liblldb_CommandObjectScript_h_ diff --git a/source/Interpreter/CommandReturnObject.cpp b/source/Interpreter/CommandReturnObject.cpp new file mode 100644 index 00000000000..9c63753a23f --- /dev/null +++ b/source/Interpreter/CommandReturnObject.cpp @@ -0,0 +1,219 @@ +//===-- CommandReturnObject.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/CommandReturnObject.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/Core/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) +{ +} + +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().Printf("%s", sstrm.GetData()); +} + +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().Printf("warning: %s", sstrm.GetData()); +} + +void +CommandReturnObject::AppendMessage (const char *in_string) +{ + if (!in_string) + return; + GetOutputStream().Printf("%s\n", in_string); +} + +void +CommandReturnObject::AppendWarning (const char *in_string) +{ + if (!in_string || *in_string == '\0') + return; + GetErrorStream().Printf("warning: %s\n", in_string); +} + +// Similar to AppendWarning, but do not prepend 'warning: ' to message, and +// don't append "\n" to the end of it. + +void +CommandReturnObject::AppendRawWarning (const char *in_string) +{ + if (in_string && in_string[0]) + GetErrorStream().PutCString(in_string); +} + +void +CommandReturnObject::AppendError (const char *in_string) +{ + if (!in_string || *in_string == '\0') + return; + GetErrorStream().Printf ("error: %s\n", in_string); +} + +void +CommandReturnObject::SetError (const Error &error, const char *fallback_error_cstr) +{ + const char *error_cstr = error.AsCString(); + if (error_cstr == NULL) + error_cstr = fallback_error_cstr; + SetError(error_cstr); +} + +void +CommandReturnObject::SetError (const char *error_cstr) +{ + if (error_cstr) + { + AppendError (error_cstr); + SetStatus (eReturnStatusFailed); + } +} + +// Similar to AppendError, but do not prepend 'Error: ' to message, and +// don't append "\n" to the end of it. + +void +CommandReturnObject::AppendRawError (const char *in_string) +{ + if (in_string && in_string[0]) + GetErrorStream().PutCString(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(stream_sp.get())->Clear(); + stream_sp = m_err_stream.GetStreamAtIndex (eStreamStringIndex); + if (stream_sp) + static_cast(stream_sp.get())->Clear(); + m_status = eReturnStatusStarted; + m_did_change_process_state = false; +} + +bool +CommandReturnObject::GetDidChangeProcessState () +{ + return m_did_change_process_state; +} + +void +CommandReturnObject::SetDidChangeProcessState (bool b) +{ + m_did_change_process_state = b; +} + diff --git a/source/Interpreter/OptionGroupArchitecture.cpp b/source/Interpreter/OptionGroupArchitecture.cpp new file mode 100644 index 00000000000..af103bb0bd9 --- /dev/null +++ b/source/Interpreter/OptionGroupArchitecture.cpp @@ -0,0 +1,86 @@ +//===-- OptionGroupArchitecture.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupArchitecture.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupArchitecture::OptionGroupArchitecture() : + m_arch_str () +{ +} + +OptionGroupArchitecture::~OptionGroupArchitecture () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 , false, "arch" , 'a', required_argument, NULL, 0, eArgTypeArchitecture , "Specify the architecture for the target."}, +}; + +uint32_t +OptionGroupArchitecture::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupArchitecture::GetDefinitions () +{ + return g_option_table; +} + +bool +OptionGroupArchitecture::GetArchitecture (Platform *platform, ArchSpec &arch) +{ + if (m_arch_str.empty()) + arch.Clear(); + else + arch.SetTriple(m_arch_str.c_str(), platform); + return arch.IsValid(); +} + + +Error +OptionGroupArchitecture::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error 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 (CommandInterpreter &interpreter) +{ + m_arch_str.clear(); +} + diff --git a/source/Interpreter/OptionGroupBoolean.cpp b/source/Interpreter/OptionGroupBoolean.cpp new file mode 100644 index 00000000000..5b5b38478b0 --- /dev/null +++ b/source/Interpreter/OptionGroupBoolean.cpp @@ -0,0 +1,67 @@ +//===-- OptionGroupBoolean.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupBoolean.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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.option_has_arg = no_argument_toggle_default ? no_argument : required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = 0; + m_option_definition.argument_type = eArgTypeBoolean; + m_option_definition.usage_text = usage_text; +} + +OptionGroupBoolean::~OptionGroupBoolean () +{ +} + +Error +OptionGroupBoolean::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + if (m_option_definition.option_has_arg == no_argument) + { + // 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.SetValueFromCString (option_arg); + } + return error; +} + +void +OptionGroupBoolean::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_value.Clear(); +} diff --git a/source/Interpreter/OptionGroupFile.cpp b/source/Interpreter/OptionGroupFile.cpp new file mode 100644 index 00000000000..6867395789c --- /dev/null +++ b/source/Interpreter/OptionGroupFile.cpp @@ -0,0 +1,97 @@ +//===-- OptionGroupFile.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupFile.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupFile::~OptionGroupFile () +{ +} + +Error +OptionGroupFile::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_file.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupFile::OptionParsingStarting (CommandInterpreter &interpreter) +{ + 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.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupFileList::~OptionGroupFileList () +{ +} + +Error +OptionGroupFileList::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_file_list.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupFileList::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_file_list.Clear(); +} diff --git a/source/Interpreter/OptionGroupFormat.cpp b/source/Interpreter/OptionGroupFormat.cpp new file mode 100644 index 00000000000..790cbb668fa --- /dev/null +++ b/source/Interpreter/OptionGroupFormat.cpp @@ -0,0 +1,249 @@ +//===-- OptionGroupFormat.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupFormat.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Utils.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 OptionDefinition +g_option_table[] = +{ +{ LLDB_OPT_SET_1, false, "format" ,'f', required_argument, NULL, 0, eArgTypeFormat , "Specify a format to be used for display."}, +{ LLDB_OPT_SET_2, false, "gdb-format",'G', required_argument, NULL, 0, eArgTypeGDBFormat, "Specify a format using a GDB format specifier string."}, +{ LLDB_OPT_SET_3, false, "size" ,'s', required_argument, NULL, 0, eArgTypeByteSize , "The size in bytes to use when displaying with the selected format."}, +{ LLDB_OPT_SET_4, false, "count" ,'c', required_argument, NULL, 0, eArgTypeCount , "The number of total items to display."}, +}; + +uint32_t +OptionGroupFormat::GetNumDefinitions () +{ + if (m_byte_size.GetDefaultValue() < UINT64_MAX) + { + if (m_count.GetDefaultValue() < UINT64_MAX) + return 4; + else + return 3; + } + return 2; +} + +const OptionDefinition * +OptionGroupFormat::GetDefinitions () +{ + return g_option_table; +} + +Error +OptionGroupFormat::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'f': + error = m_format.SetValueFromCString (option_arg); + break; + + case 'c': + if (m_count.GetDefaultValue() == 0) + { + error.SetErrorString ("--count option is disabled"); + } + else + { + error = m_count.SetValueFromCString (option_arg); + if (m_count.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --count option value '%s'", option_arg); + } + break; + + case 's': + if (m_byte_size.GetDefaultValue() == 0) + { + error.SetErrorString ("--size option is disabled"); + } + else + { + error = m_byte_size.SetValueFromCString (option_arg); + if (m_byte_size.GetCurrentValue() == 0) + error.SetErrorStringWithFormat("invalid --size option value '%s'", option_arg); + } + break; + + case 'G': + { + char *end = NULL; + const char *gdb_format_cstr = option_arg; + uint64_t count = 0; + if (::isdigit (gdb_format_cstr[0])) + { + count = strtoull (gdb_format_cstr, &end, 0); + + if (option_arg != end) + gdb_format_cstr = end; // We have a valid count, advance the string position + else + count = 0; + } + + Format format = eFormatDefault; + uint32_t byte_size = 0; + + while (ParserGDBFormatLetter (interpreter, gdb_format_cstr[0], format, byte_size)) + { + ++gdb_format_cstr; + } + + // We the first character of the "gdb_format_cstr" 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_cstr[0] || (format == eFormatInvalid && byte_size == 0 && count == 0)) + { + // Nothing got set correctly + error.SetErrorStringWithFormat ("invalid gdb format string '%s'", option_arg); + 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 (interpreter, 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 (interpreter, m_prev_gdb_size, format, byte_size); + } + else + { + // Byte size is disabled, make sure it wasn't specified + if (byte_size > 0) + { + 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 (CommandInterpreter &interpreter, 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; + { + ExecutionContext exe_ctx(interpreter.GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + byte_size = target->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': byte_size = 1; m_prev_gdb_size = format_letter; return true; + case 'h': byte_size = 2; m_prev_gdb_size = format_letter; return true; + case 'w': byte_size = 4; m_prev_gdb_size = format_letter; return true; + case 'g': byte_size = 8; m_prev_gdb_size = format_letter; return true; + default: break; + } + return false; +} + +void +OptionGroupFormat::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_format.Clear(); + m_byte_size.Clear(); + m_count.Clear(); + m_has_gdb_format = false; +} diff --git a/source/Interpreter/OptionGroupOutputFile.cpp b/source/Interpreter/OptionGroupOutputFile.cpp new file mode 100644 index 00000000000..aa01bf54964 --- /dev/null +++ b/source/Interpreter/OptionGroupOutputFile.cpp @@ -0,0 +1,81 @@ +//===-- OptionGroupOutputFile.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupOutputFile.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupOutputFile::OptionGroupOutputFile() : + m_file (), + m_append (false, false) +{ +} + +OptionGroupOutputFile::~OptionGroupOutputFile () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 , false, "outfile", 'o', required_argument, NULL, 0, eArgTypeFilename , "Specify a path for capturing command output."}, + { LLDB_OPT_SET_1 , false, "append-outfile" , 'apnd', no_argument, NULL, 0, eArgTypeNone , "Append to the the file specified with '--outfile '."}, +}; + +uint32_t +OptionGroupOutputFile::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupOutputFile::GetDefinitions () +{ + return g_option_table; +} + +Error +OptionGroupOutputFile::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'o': + error = m_file.SetValueFromCString (option_arg); + break; + + case 'apnd': + m_append.SetCurrentValue(true); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupOutputFile::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_file.Clear(); + m_append.Clear(); +} diff --git a/source/Interpreter/OptionGroupPlatform.cpp b/source/Interpreter/OptionGroupPlatform.cpp new file mode 100644 index 00000000000..a54edafd019 --- /dev/null +++ b/source/Interpreter/OptionGroupPlatform.cpp @@ -0,0 +1,149 @@ +//===-- OptionGroupPlatform.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupPlatform.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Platform.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +PlatformSP +OptionGroupPlatform::CreatePlatformWithOptions (CommandInterpreter &interpreter, + const ArchSpec &arch, + bool make_selected, + Error& error, + ArchSpec &platform_arch) const +{ + PlatformSP platform_sp; + + if (!m_platform_name.empty()) + { + platform_sp = Platform::Create (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_major != UINT32_MAX) + { + platform_sp->SetOSVersion (m_os_version_major, + m_os_version_minor, + m_os_version_update); + } + + 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 (CommandInterpreter &interpreter) +{ + m_platform_name.clear(); + m_sdk_sysroot.Clear(); + m_sdk_build.Clear(); + m_os_version_major = UINT32_MAX; + m_os_version_minor = UINT32_MAX; + m_os_version_update = UINT32_MAX; +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_ALL, false, "platform", 'p', required_argument, NULL, 0, eArgTypePlatform, "Specify name of the platform to use for this target, creating the platform if necessary."}, + { LLDB_OPT_SET_ALL, false, "version" , 'v', required_argument, NULL, 0, eArgTypeNone, "Specify the initial SDK version to use prior to connecting." }, + { LLDB_OPT_SET_ALL, false, "build" , 'b', required_argument, NULL, 0, eArgTypeNone, "Specify the initial SDK build number." }, + { LLDB_OPT_SET_ALL, false, "sysroot" , 'S', required_argument, NULL, 0, eArgTypeFilename, "Specify the SDK root directory that contains a root of all remote system files." } +}; + +const OptionDefinition* +OptionGroupPlatform::GetDefinitions () +{ + if (m_include_platform_option) + return g_option_table; + return g_option_table + 1; +} + +uint32_t +OptionGroupPlatform::GetNumDefinitions () +{ + if (m_include_platform_option) + return llvm::array_lengthof(g_option_table); + return llvm::array_lengthof(g_option_table) - 1; +} + + +Error +OptionGroupPlatform::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error 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 (Args::StringToVersion (option_arg, + m_os_version_major, + m_os_version_minor, + m_os_version_update) == option_arg) + error.SetErrorStringWithFormat ("invalid version string '%s'", option_arg); + break; + + case 'b': + m_sdk_build.SetCString (option_arg); + break; + + case 'S': + m_sdk_sysroot.SetCString (option_arg); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + return error; +} diff --git a/source/Interpreter/OptionGroupString.cpp b/source/Interpreter/OptionGroupString.cpp new file mode 100644 index 00000000000..ee9623967c6 --- /dev/null +++ b/source/Interpreter/OptionGroupString.cpp @@ -0,0 +1,58 @@ +//===-- OptionGroupString.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupString.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupString::~OptionGroupString () +{ +} + +Error +OptionGroupString::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_value.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupString::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_value.Clear(); +} diff --git a/source/Interpreter/OptionGroupUInt64.cpp b/source/Interpreter/OptionGroupUInt64.cpp new file mode 100644 index 00000000000..e6996f70255 --- /dev/null +++ b/source/Interpreter/OptionGroupUInt64.cpp @@ -0,0 +1,58 @@ +//===-- OptionGroupUInt64.cpp ----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupUInt64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes + +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.option_has_arg = required_argument; + m_option_definition.enum_values = NULL; + m_option_definition.completion_type = completion_type; + m_option_definition.argument_type = argument_type; + m_option_definition.usage_text = usage_text; +} + +OptionGroupUInt64::~OptionGroupUInt64 () +{ +} + +Error +OptionGroupUInt64::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error (m_value.SetValueFromCString (option_arg)); + return error; +} + +void +OptionGroupUInt64::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_value.Clear(); +} diff --git a/source/Interpreter/OptionGroupUUID.cpp b/source/Interpreter/OptionGroupUUID.cpp new file mode 100644 index 00000000000..14bdc8494e4 --- /dev/null +++ b/source/Interpreter/OptionGroupUUID.cpp @@ -0,0 +1,76 @@ +//===-- OptionGroupUUID.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupUUID.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupUUID::OptionGroupUUID() : + m_uuid () +{ +} + +OptionGroupUUID::~OptionGroupUUID () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 , false, "uuid", 'u', required_argument, NULL, 0, eArgTypeNone, "A module UUID value."}, +}; + +uint32_t +OptionGroupUUID::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupUUID::GetDefinitions () +{ + return g_option_table; +} + +Error +OptionGroupUUID::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + + switch (short_option) + { + case 'u': + error = m_uuid.SetValueFromCString (option_arg); + if (error.Success()) + m_uuid.SetOptionWasSet(); + break; + + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupUUID::OptionParsingStarting (CommandInterpreter &interpreter) +{ + m_uuid.Clear(); +} diff --git a/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/source/Interpreter/OptionGroupValueObjectDisplay.cpp new file mode 100644 index 00000000000..22a7f37740d --- /dev/null +++ b/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -0,0 +1,181 @@ +//===-- OptionGroupValueObjectDisplay.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Target/Target.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +OptionGroupValueObjectDisplay::OptionGroupValueObjectDisplay() +{ +} + +OptionGroupValueObjectDisplay::~OptionGroupValueObjectDisplay () +{ +} + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "dynamic-type", 'd', required_argument, g_dynamic_value_types, 0, eArgTypeNone, "Show the object as its full dynamic type, not its static type, if available."}, + { LLDB_OPT_SET_1, false, "synthetic-type", 'S', required_argument, NULL, 0, eArgTypeBoolean, "Show the object obeying its synthetic provider, if available."}, + { LLDB_OPT_SET_1, false, "depth", 'D', required_argument, NULL, 0, eArgTypeCount, "Set the max recurse depth when dumping aggregate types (default is infinity)."}, + { LLDB_OPT_SET_1, false, "flat", 'F', no_argument, NULL, 0, eArgTypeNone, "Display results in a flat format that uses expression paths for each variable or member."}, + { LLDB_OPT_SET_1, false, "location", 'L', no_argument, NULL, 0, eArgTypeNone, "Show variable location information."}, + { LLDB_OPT_SET_1, false, "object-description", 'O', no_argument, NULL, 0, eArgTypeNone, "Print as an Objective-C object."}, + { LLDB_OPT_SET_1, false, "ptr-depth", 'P', required_argument, NULL, 0, eArgTypeCount, "The number of pointers to be traversed when dumping values (default is zero)."}, + { LLDB_OPT_SET_1, false, "show-types", 'T', no_argument, NULL, 0, eArgTypeNone, "Show variable types when dumping values."}, + { LLDB_OPT_SET_1, false, "no-summary-depth", 'Y', optional_argument, NULL, 0, eArgTypeCount, "Set the depth at which omitting summary information stops (default is 1)."}, + { LLDB_OPT_SET_1, false, "raw-output", 'R', no_argument, NULL, 0, eArgTypeNone, "Don't use formatting options."}, + { LLDB_OPT_SET_1, false, "show-all-children", 'A', no_argument, NULL, 0, eArgTypeNone, "Ignore the upper bound on the number of children to show."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + +uint32_t +OptionGroupValueObjectDisplay::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} + +const OptionDefinition * +OptionGroupValueObjectDisplay::GetDefinitions () +{ + return g_option_table; +} + + +Error +OptionGroupValueObjectDisplay::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + const int short_option = g_option_table[option_idx].short_option; + bool success = false; + + switch (short_option) + { + case 'd': + { + int32_t result; + result = Args::StringToOptionEnum (option_arg, g_dynamic_value_types, 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': + max_depth = Args::StringToUInt32 (option_arg, UINT32_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid max depth '%s'", option_arg); + break; + + case 'P': + ptr_depth = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid pointer depth '%s'", option_arg); + break; + + case 'Y': + if (option_arg) + { + no_summary_depth = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("invalid pointer depth '%s'", option_arg); + } + else + no_summary_depth = 1; + break; + + case 'S': + use_synth = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("invalid synthetic-type '%s'", option_arg); + break; + default: + error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option); + break; + } + + return error; +} + +void +OptionGroupValueObjectDisplay::OptionParsingStarting (CommandInterpreter &interpreter) +{ + // 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; + use_synth = true; + be_raw = false; + ignore_cap = false; + + Target *target = interpreter.GetExecutionContext().GetTargetPtr(); + if (target != NULL) + use_dynamic = target->GetPreferDynamicValue(); + else + { + // If we don't have any targets, then dynamic values won't do us much good. + use_dynamic = lldb::eNoDynamicValues; + } +} + +ValueObject::DumpValueObjectOptions +OptionGroupValueObjectDisplay::GetAsDumpOptions (bool objc_is_compact, + lldb::Format format, + lldb::TypeSummaryImplSP summary_sp) +{ + ValueObject::DumpValueObjectOptions options; + options.SetMaximumPointerDepth(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 (objc_is_compact) + options.SetHideRootType(use_objc) + .SetHideName(use_objc) + .SetHideValue(use_objc); + + if (be_raw) + options.SetRawDisplay(true); + + return options; +} diff --git a/source/Interpreter/OptionGroupVariable.cpp b/source/Interpreter/OptionGroupVariable.cpp new file mode 100644 index 00000000000..316747eff03 --- /dev/null +++ b/source/Interpreter/OptionGroupVariable.cpp @@ -0,0 +1,144 @@ +//===-- OptionGroupVariable.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionGroupVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Error.h" +#include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Target/Target.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +// if you add any options here, remember to update the counters in OptionGroupVariable::GetNumDefinitions() +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-args", 'a', no_argument, NULL, 0, eArgTypeNone, "Omit function arguments."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "no-locals", 'l', no_argument, NULL, 0, eArgTypeNone, "Omit local variables."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-globals", 'g', no_argument, NULL, 0, eArgTypeNone, "Show the current frame source file global and static variables."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "show-declaration",'c', no_argument, NULL, 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', no_argument, NULL, 0, eArgTypeRegularExpression, "The argument for name lookups are regular expressions."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "scope", 's', no_argument, NULL, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."}, + { LLDB_OPT_SET_1, false, "summary", 'y', required_argument, NULL, 0, eArgTypeName, "Specify the summary that the variable output should use."}, + { LLDB_OPT_SET_2, false, "summary-string", 'z', required_argument, NULL, 0, eArgTypeName, "Specify a summary string to use to format the variable output."}, +}; + +static Error +ValidateNamedSummary (const char* str, void*) +{ + if (!str || !str[0]) + return Error("must specify a valid named summary"); + TypeSummaryImplSP summary_sp; + if (DataVisualization::NamedSummaryFormats::GetSummaryFormat(ConstString(str), summary_sp) == false) + return Error("must specify a valid named summary"); + return Error(); +} + +static Error +ValidateSummaryString (const char* str, void*) +{ + if (!str || !str[0]) + return Error("must specify a non-empty summary string"); + return Error(); +} + +OptionGroupVariable::OptionGroupVariable (bool show_frame_options) : + OptionGroup(), + include_frame_options (show_frame_options), + summary(ValidateNamedSummary), + summary_string(ValidateSummaryString) +{ +} + +OptionGroupVariable::~OptionGroupVariable () +{ +} + +Error +OptionGroupVariable::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error error; + if (!include_frame_options) + option_idx += 3; + const int short_option = g_option_table[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 '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 (CommandInterpreter &interpreter) +{ + show_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 + +const OptionDefinition* +OptionGroupVariable::GetDefinitions () +{ + // Show the "--no-args", "--no-locals" and "--show-globals" + // options if we are showing frame specific options + if (include_frame_options) + return g_option_table; + + // Skip the "--no-args", "--no-locals" and "--show-globals" + // options if we are not showing frame specific options (globals only) + return &g_option_table[NUM_FRAME_OPTS]; +} + +uint32_t +OptionGroupVariable::GetNumDefinitions () +{ + // Count the "--no-args", "--no-locals" and "--show-globals" + // options if we are showing frame specific options. + if (include_frame_options) + return llvm::array_lengthof(g_option_table); + else + return llvm::array_lengthof(g_option_table) - NUM_FRAME_OPTS; +} + + diff --git a/source/Interpreter/OptionGroupWatchpoint.cpp b/source/Interpreter/OptionGroupWatchpoint.cpp new file mode 100644 index 00000000000..9eef37a3ae6 --- /dev/null +++ b/source/Interpreter/OptionGroupWatchpoint.cpp @@ -0,0 +1,121 @@ +//===-- OptionGroupWatchpoint.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionGroupWatchpoint.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-enumerations.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Utility/Utils.h" + +using namespace lldb; +using namespace lldb_private; + +static OptionEnumValueElement g_watch_type[] = +{ + { OptionGroupWatchpoint::eWatchRead, "read", "Watch for read"}, + { OptionGroupWatchpoint::eWatchWrite, "write", "Watch for write"}, + { OptionGroupWatchpoint::eWatchReadWrite, "read_write", "Watch for read/write"}, + { 0, NULL, NULL } +}; + +static 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"}, + { 0, NULL, NULL } +}; + +static OptionDefinition +g_option_table[] = +{ + { LLDB_OPT_SET_1, false, "watch", 'w', required_argument, g_watch_type, 0, eArgTypeWatchType, "Specify the type of watching to perform."}, + { LLDB_OPT_SET_1, false, "xsize", 'x', required_argument, g_watch_size, 0, eArgTypeByteSize, "Number of bytes to use to watch a region."} +}; + + +bool +OptionGroupWatchpoint::IsWatchSizeSupported(uint32_t watch_size) +{ + for (uint32_t i = 0; i < llvm::array_lengthof(g_watch_size); ++i) + { + if (g_watch_size[i].value == 0) + break; + if (watch_size == g_watch_size[i].value) + return true; + } + return false; +} + +OptionGroupWatchpoint::OptionGroupWatchpoint () : + OptionGroup() +{ +} + +OptionGroupWatchpoint::~OptionGroupWatchpoint () +{ +} + +Error +OptionGroupWatchpoint::SetOptionValue (CommandInterpreter &interpreter, + uint32_t option_idx, + const char *option_arg) +{ + Error 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) Args::StringToOptionEnum(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 'x': + watch_size = (uint32_t) Args::StringToOptionEnum(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 (CommandInterpreter &interpreter) +{ + watch_type_specified = false; + watch_type = eWatchInvalid; + watch_size = 0; +} + + +const OptionDefinition* +OptionGroupWatchpoint::GetDefinitions () +{ + return g_option_table; +} + +uint32_t +OptionGroupWatchpoint::GetNumDefinitions () +{ + return llvm::array_lengthof(g_option_table); +} diff --git a/source/Interpreter/OptionValue.cpp b/source/Interpreter/OptionValue.cpp new file mode 100644 index 00000000000..1f6b03ddac1 --- /dev/null +++ b/source/Interpreter/OptionValue.cpp @@ -0,0 +1,633 @@ +//===-- OptionValue.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValue.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/OptionValues.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(this)->GetCurrentValue(); + case OptionValue::eTypeSInt64: return static_cast(this)->GetCurrentValue(); + case OptionValue::eTypeUInt64: return static_cast(this)->GetCurrentValue(); + default: + break; + } + if (success_ptr) + *success_ptr = false; + return fail_value; +} + +Error +OptionValue::SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value) +{ + Error error; + error.SetErrorStringWithFormat("SetSubValue is not supported"); + return error; +} + + +OptionValueBoolean * +OptionValue::GetAsBoolean () +{ + if (GetType () == OptionValue::eTypeBoolean) + return static_cast(this); + return NULL; +} + +const OptionValueBoolean * +OptionValue::GetAsBoolean () const +{ + if (GetType () == OptionValue::eTypeBoolean) + return static_cast(this); + return NULL; +} + + +OptionValueFileSpec * +OptionValue::GetAsFileSpec () +{ + if (GetType () == OptionValue::eTypeFileSpec) + return static_cast(this); + return NULL; + +} + +const OptionValueFileSpec * +OptionValue::GetAsFileSpec () const +{ + if (GetType () == OptionValue::eTypeFileSpec) + return static_cast(this); + return NULL; + +} + +OptionValueFileSpecList * +OptionValue::GetAsFileSpecList () +{ + if (GetType () == OptionValue::eTypeFileSpecList) + return static_cast(this); + return NULL; + +} + +const OptionValueFileSpecList * +OptionValue::GetAsFileSpecList () const +{ + if (GetType () == OptionValue::eTypeFileSpecList) + return static_cast(this); + return NULL; + +} + +OptionValueArch * +OptionValue::GetAsArch () +{ + if (GetType () == OptionValue::eTypeArch) + return static_cast(this); + return NULL; +} + + +const OptionValueArch * +OptionValue::GetAsArch () const +{ + if (GetType () == OptionValue::eTypeArch) + return static_cast(this); + return NULL; +} + +OptionValueArray * +OptionValue::GetAsArray () +{ + if (GetType () == OptionValue::eTypeArray) + return static_cast(this); + return NULL; +} + + +const OptionValueArray * +OptionValue::GetAsArray () const +{ + if (GetType () == OptionValue::eTypeArray) + return static_cast(this); + return NULL; +} + +OptionValueArgs * +OptionValue::GetAsArgs () +{ + if (GetType () == OptionValue::eTypeArgs) + return static_cast(this); + return NULL; +} + + +const OptionValueArgs * +OptionValue::GetAsArgs () const +{ + if (GetType () == OptionValue::eTypeArgs) + return static_cast(this); + return NULL; +} + +OptionValueDictionary * +OptionValue::GetAsDictionary () +{ + if (GetType () == OptionValue::eTypeDictionary) + return static_cast(this); + return NULL; +} + +const OptionValueDictionary * +OptionValue::GetAsDictionary () const +{ + if (GetType () == OptionValue::eTypeDictionary) + return static_cast(this); + return NULL; +} + +OptionValueEnumeration * +OptionValue::GetAsEnumeration () +{ + if (GetType () == OptionValue::eTypeEnum) + return static_cast(this); + return NULL; +} + +const OptionValueEnumeration * +OptionValue::GetAsEnumeration () const +{ + if (GetType () == OptionValue::eTypeEnum) + return static_cast(this); + return NULL; +} + +OptionValueFormat * +OptionValue::GetAsFormat () +{ + if (GetType () == OptionValue::eTypeFormat) + return static_cast(this); + return NULL; +} + +const OptionValueFormat * +OptionValue::GetAsFormat () const +{ + if (GetType () == OptionValue::eTypeFormat) + return static_cast(this); + return NULL; +} + +OptionValuePathMappings * +OptionValue::GetAsPathMappings () +{ + if (GetType () == OptionValue::eTypePathMap) + return static_cast(this); + return NULL; +} + +const OptionValuePathMappings * +OptionValue::GetAsPathMappings () const +{ + if (GetType () == OptionValue::eTypePathMap) + return static_cast(this); + return NULL; +} + +OptionValueProperties * +OptionValue::GetAsProperties () +{ + if (GetType () == OptionValue::eTypeProperties) + return static_cast(this); + return NULL; +} + +const OptionValueProperties * +OptionValue::GetAsProperties () const +{ + if (GetType () == OptionValue::eTypeProperties) + return static_cast(this); + return NULL; +} + +OptionValueRegex * +OptionValue::GetAsRegex () +{ + if (GetType () == OptionValue::eTypeRegex) + return static_cast(this); + return NULL; +} + +const OptionValueRegex * +OptionValue::GetAsRegex () const +{ + if (GetType () == OptionValue::eTypeRegex) + return static_cast(this); + return NULL; +} + +OptionValueSInt64 * +OptionValue::GetAsSInt64 () +{ + if (GetType () == OptionValue::eTypeSInt64) + return static_cast(this); + return NULL; +} + +const OptionValueSInt64 * +OptionValue::GetAsSInt64 () const +{ + if (GetType () == OptionValue::eTypeSInt64) + return static_cast(this); + return NULL; +} + +OptionValueString * +OptionValue::GetAsString () +{ + if (GetType () == OptionValue::eTypeString) + return static_cast(this); + return NULL; +} + +const OptionValueString * +OptionValue::GetAsString () const +{ + if (GetType () == OptionValue::eTypeString) + return static_cast(this); + return NULL; +} + +OptionValueUInt64 * +OptionValue::GetAsUInt64 () +{ + if (GetType () == OptionValue::eTypeUInt64) + return static_cast(this); + return NULL; +} + +const OptionValueUInt64 * +OptionValue::GetAsUInt64 () const +{ + if (GetType () == OptionValue::eTypeUInt64) + return static_cast(this); + return NULL; +} + +OptionValueUUID * +OptionValue::GetAsUUID () +{ + if (GetType () == OptionValue::eTypeUUID) + return static_cast(this); + return NULL; + +} + +const OptionValueUUID * +OptionValue::GetAsUUID () const +{ + if (GetType () == OptionValue::eTypeUUID) + return static_cast(this); + return NULL; + +} + +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; +} + +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; +} + +const RegularExpression * +OptionValue::GetRegexValue () const +{ + const OptionValueRegex *option_value = GetAsRegex (); + if (option_value) + return option_value->GetCurrentValue(); + return NULL; +} + + +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; +} + +const char * +OptionValue::GetStringValue (const char *fail_value) const +{ + const OptionValueString *option_value = GetAsString (); + if (option_value) + return option_value->GetCurrentValue(); + return fail_value; +} + +bool +OptionValue::SetStringValue (const char *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 eTypeDictionary: return "dictionary"; + case eTypeEnum: return "enum"; + case eTypeFileSpec: return "file"; + case eTypeFileSpecList: return "file-list"; + case eTypeFormat: return "format"; + 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 NULL; +} + + +lldb::OptionValueSP +OptionValue::CreateValueFromCStringForTypeMask (const char *value_cstr, uint32_t type_mask, Error &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 << eTypeFileSpec: value_sp.reset(new OptionValueFileSpec()); break; + case 1u << eTypeFormat: value_sp.reset(new OptionValueFormat(eFormatInvalid)); 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->SetValueFromCString (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, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + return matches.GetSize(); +} + +Error +OptionValue::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Error 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/source/Interpreter/OptionValueArch.cpp b/source/Interpreter/OptionValueArch.cpp new file mode 100644 index 00000000000..92fedffe75e --- /dev/null +++ b/source/Interpreter/OptionValueArch.cpp @@ -0,0 +1,111 @@ +//===-- OptionValueArch.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionValueArch.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/State.h" +#include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandCompletions.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); + } + } +} + +Error +OptionValueArch::SetValueFromCString (const char *value_cstr, VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (value_cstr && value_cstr[0]) + { + if (m_current_value.SetTriple (value_cstr)) + m_value_was_set = true; + else + error.SetErrorStringWithFormat("unsupported architecture '%s'", value_cstr); + } + else + { + error.SetErrorString("invalid value string"); + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueArch::DeepCopy () const +{ + return OptionValueSP(new OptionValueArch(*this)); +} + + +size_t +OptionValueArch::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + CommandCompletions::InvokeCommonCompletionCallbacks (interpreter, + CommandCompletions::eArchitectureCompletion, + s, + match_start_point, + max_return_elements, + NULL, + word_complete, + matches); + return matches.GetSize(); +} + + + + diff --git a/source/Interpreter/OptionValueArgs.cpp b/source/Interpreter/OptionValueArgs.cpp new file mode 100644 index 00000000000..e28d884581f --- /dev/null +++ b/source/Interpreter/OptionValueArgs.cpp @@ -0,0 +1,38 @@ +//===-- OptionValueArgs.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueArgs.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +size_t +OptionValueArgs::GetArgs (Args &args) +{ + const uint32_t size = m_values.size(); + std::vector argv; + for (uint32_t i = 0; iGetStringValue (); + if (string_value) + argv.push_back(string_value); + } + + if (argv.empty()) + args.Clear(); + else + args.SetArguments(argv.size(), &argv[0]); + return args.GetArgumentCount(); +} diff --git a/source/Interpreter/OptionValueArray.cpp b/source/Interpreter/OptionValueArray.cpp new file mode 100644 index 00000000000..9a015580bd6 --- /dev/null +++ b/source/Interpreter/OptionValueArray.cpp @@ -0,0 +1,350 @@ +//===-- OptionValueArray.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueArray.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.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) + { + if (dump_mask & eDumpOptionType) + strm.Printf (" =%s", (m_values.size() > 0) ? "\n" : ""); + strm.IndentMore(); + const uint32_t size = m_values.size(); + for (uint32_t i = 0; iDumpValue(exe_ctx, strm, dump_mask | extra_dump_options); + break; + + case eTypeBoolean: + 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 (i < (size - 1)) + strm.EOL(); + } + strm.IndentLess(); + } +} + +Error +OptionValueArray::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Args args(value); + return SetArgs (args, op); +} + + +lldb::OptionValueSP +OptionValueArray::GetSubValue (const ExecutionContext *exe_ctx, + const char *name, + bool will_modify, + Error &error) const +{ + if (name && name[0] == '[') + { + const char *end_bracket = strchr (name+1, ']'); + if (end_bracket) + { + const char *sub_value = NULL; + if (end_bracket[1]) + sub_value = end_bracket + 1; + std::string index_str (name+1, end_bracket); + const size_t array_count = m_values.size(); + int32_t idx = Args::StringToSInt32(index_str.c_str(), INT32_MAX, 0, NULL); + if (idx != INT32_MAX) + { + ; + 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) + 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); + } + } + } + } + else + { + error.SetErrorStringWithFormat("invalid value path '%s', %s values only support '[]' subvalues where is a positive or negative array index", name, GetTypeAsCString()); + } + return OptionValueSP(); +} + + +size_t +OptionValueArray::GetArgs (Args &args) const +{ + const uint32_t size = m_values.size(); + std::vector argv; + for (uint32_t i = 0; iGetStringValue (); + if (string_value) + argv.push_back(string_value); + } + + if (argv.empty()) + args.Clear(); + else + args.SetArguments(argv.size(), &argv[0]); + return args.GetArgumentCount(); +} + +Error +OptionValueArray::SetArgs (const Args &args, VarSetOperationType op) +{ + Error 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 = Args::StringToUInt32(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= 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 remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i=0; i= 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) + { + std::sort(remove_indexes.begin(), remove_indexes.end()); + for (std::vector::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 = Args::StringToUInt32(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; iAppendValue (m_values[i]->DeepCopy()); + } + return copied_value_sp; +} + + + diff --git a/source/Interpreter/OptionValueBoolean.cpp b/source/Interpreter/OptionValueBoolean.cpp new file mode 100644 index 00000000000..6471943ee5d --- /dev/null +++ b/source/Interpreter/OptionValueBoolean.cpp @@ -0,0 +1,135 @@ +//===-- OptionValueBoolean.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueBoolean.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/Args.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"); + } +} + +Error +OptionValueBoolean::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + bool value = Args::StringToBoolean(value_cstr, false, &success); + if (success) + { + m_value_was_set = true; + m_current_value = value; + } + else + { + if (value_cstr == NULL) + error.SetErrorString ("invalid boolean string value: NULL"); + else if (value_cstr[0] == '\0') + error.SetErrorString ("invalid boolean string value "); + else + error.SetErrorStringWithFormat ("invalid boolean string value: '%s'", value_cstr); + } + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueBoolean::DeepCopy () const +{ + return OptionValueSP(new OptionValueBoolean(*this)); +} + +size_t +OptionValueBoolean::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + struct StringEntry { + const char *string; + const size_t length; + }; + static const StringEntry g_autocomplete_entries[] = + { + { "true" , 4 }, + { "false", 5 }, + { "on" , 2 }, + { "off" , 3 }, + { "yes" , 3 }, + { "no" , 2 }, + { "1" , 1 }, + { "0" , 1 }, + }; + const size_t k_num_autocomplete_entries = sizeof(g_autocomplete_entries)/sizeof(StringEntry); + + if (s && s[0]) + { + const size_t s_len = strlen(s); + for (size_t i=0; iDumpValue(exe_ctx, strm, (dump_mask & (~eDumpOptionType)) | extra_dump_options); + break; + } + } + 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(NULL, strm, eDumpOptionValue|eDumpOptionRaw); + args.AppendArgument(strm.GetString().c_str()); + } + return args.GetArgumentCount(); +} + +Error +OptionValueDictionary::SetArgs (const Args &args, VarSetOperationType op) +{ + Error error; + const size_t argc = args.GetArgumentCount(); + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationAppend: + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (argc > 0) + { + for (size_t i=0; i kvp(key_and_value.split('=')); + llvm::StringRef key = kvp.first; + bool key_valid = false; + if (!key.empty()) + { + if (key.front() == '[') + { + // Key name starts with '[', so the the key value must be in single or double quotes like: + // [''] + // [""] + 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: [] or [''] or [\"\"]", kvp.first.str().c_str()); + return error; + } + + lldb::OptionValueSP value_sp (CreateValueFromCStringForTypeMask (kvp.second.data(), + 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"); + } + } + else + { + error.SetErrorString("empty argument"); + } + } + } + else + { + error.SetErrorString("assign operation takes one or more key=value arguments"); + } + break; + + case eVarSetOperationRemove: + if (argc > 0) + { + for (size_t i=0; i'] where is a string that doesn't contain quotes", name); + return value_sp; + } + } + else + { + error.SetErrorString ("missing '] key name terminator, key name started with ['"); + return value_sp; + } + break; + case '"': + ++key_start; + key_end = strchr(key_start, '"'); + if (key_end) + { + if (key_end[1] == ']') + { + if (key_end[2]) + sub_name = key_end + 2; + break; + } + error.SetErrorStringWithFormat ("invalid value path '%s', double quoted key names must be formatted as [\"\"] where is a string that doesn't contain quotes", name); + return value_sp; + } + else + { + error.SetErrorString ("missing \"] key name terminator, key name started with [\""); + return value_sp; + } + break; + + default: + key_end = strchr(key_start, ']'); + if (key_end) + { + if (key_end[1]) + sub_name = key_end + 1; + } + else + { + error.SetErrorString ("missing ] key name terminator, key name started with ["); + return value_sp; + } + break; + } + + if (key_start && key_end) + { + key.SetCStringWithLength (key_start, key_end - key_start); + + value_sp = GetValueForKey (key); + if (value_sp) + { + if (sub_name) + return value_sp->GetSubValue (exe_ctx, sub_name, will_modify, error); + } + else + { + error.SetErrorStringWithFormat("dictionary does not contain a value for the key name '%s'", key.GetCString()); + } + } + } + if (!value_sp && error.AsCString() == NULL) + { + error.SetErrorStringWithFormat ("invalid value path '%s', %s values only support '[]' subvalues where a string value optionally delimitted by single or double quotes", + name, + GetTypeAsCString()); + } + } + return value_sp; +} + +Error +OptionValueDictionary::SetSubValue (const ExecutionContext *exe_ctx, VarSetOperationType op, const char *name, const char *value) +{ + Error error; + const bool will_modify = true; + lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error)); + if (value_sp) + error = value_sp->SetValueFromCString(value, op); + else + { + if (error.AsCString() == NULL) + error.SetErrorStringWithFormat("invalid value path '%s'", name); + } + return error; +} + + +lldb::OptionValueSP +OptionValueDictionary::GetValueForKey (const 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; +} + +const char * +OptionValueDictionary::GetStringValueForKey (const ConstString &key) +{ + collection::const_iterator pos = m_values.find (key); + if (pos != m_values.end()) + { + OptionValueString *string_value = pos->second->GetAsString(); + if (string_value) + return string_value->GetCurrentValue(); + } + return NULL; +} + + +bool +OptionValueDictionary::SetStringValueForKey (const ConstString &key, + const char *value, + bool can_replace) +{ + collection::const_iterator pos = m_values.find (key); + if (pos != m_values.end()) + { + if (!can_replace) + return false; + if (pos->second->GetType() == OptionValue::eTypeString) + { + pos->second->SetValueFromCString(value); + return true; + } + } + m_values[key] = OptionValueSP (new OptionValueString (value)); + return true; + +} + +bool +OptionValueDictionary::SetValueForKey (const 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 (const 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/source/Interpreter/OptionValueEnumeration.cpp b/source/Interpreter/OptionValueEnumeration.cpp new file mode 100644 index 00000000000..f282235d58e --- /dev/null +++ b/source/Interpreter/OptionValueEnumeration.cpp @@ -0,0 +1,166 @@ +//===-- OptionValueEnumeration.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueEnumeration.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/StringList.h" + +using namespace lldb; +using namespace lldb_private; + +OptionValueEnumeration::OptionValueEnumeration (const OptionEnumValueElement *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; ivalue.value; + } + else + { + StreamString error_strm; + error_strm.Printf("invalid enumeration value '%s'", value); + const size_t count = m_enumerations.GetSize (); + if (count) + { + error_strm.Printf(", valid values are: %s", m_enumerations.GetCStringAtIndex(0)); + for (size_t i=1; i 0 ? "\n" : ""); + strm.IndentMore(); + const uint32_t size = m_current_value.GetSize(); + for (uint32_t i = 0; i 1) + { + uint32_t idx = Args::StringToUInt32(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 0) + { + m_value_was_set = true; + for (size_t i=0; i 1) + { + uint32_t idx = Args::StringToUInt32(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 0) + { + std::vector remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i=0; all_indexes_valid && i 0) ? "\n" : ""); + m_path_mappings.Dump(&strm); + } +} + +Error +OptionValuePathMappings::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Error error; + Args args(value); + const size_t argc = args.GetArgumentCount(); + + switch (op) + { + case eVarSetOperationClear: + Clear (); + 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 = Args::StringToUInt32(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 + { + for (size_t i=1; i= 3 && (((argc - 1) & 1) == 0)) + { + uint32_t idx = Args::StringToUInt32(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 + { + if (op == eVarSetOperationInsertAfter) + ++idx; + for (size_t i=1; i 0) + { + std::vector remove_indexes; + bool all_indexes_valid = true; + size_t i; + for (i=0; all_indexes_valid && iDeepCopy()); + m_properties[i].SetOptionValue(new_value_sp); + } + } +} + + + +size_t +OptionValueProperties::GetNumProperties() const +{ + return m_properties.size(); +} + + +void +OptionValueProperties::Initialize (const PropertyDefinition *defs) +{ + for (size_t i=0; defs[i].name; ++i) + { + Property property(defs[i]); + assert(property.IsValid()); + m_name_to_index.Append(property.GetName().GetCString(),m_properties.size()); + property.GetValue()->SetParent(shared_from_this()); + m_properties.push_back(property); + } + m_name_to_index.Sort(); +} + +void +OptionValueProperties::AppendProperty(const ConstString &name, + const ConstString &desc, + bool is_global, + const OptionValueSP &value_sp) +{ + Property property(name, desc, is_global, value_sp); + m_name_to_index.Append(name.GetCString(),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, + const ConstString &key, + bool will_modify) const +{ + lldb::OptionValueSP value_sp; + size_t idx = m_name_to_index.Find (key.GetCString(), 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, + const char *name, + bool will_modify, + Error &error) const +{ + lldb::OptionValueSP value_sp; + + if (name && name[0]) + { + const char *sub_name = NULL; + ConstString key; + size_t key_len = ::strcspn (name, ".[{"); + + if (name[key_len]) + { + key.SetCStringWithLength (name, key_len); + sub_name = name + key_len; + } + else + key.SetCString (name); + + value_sp = GetValueForKey (exe_ctx, key, will_modify); + if (sub_name && value_sp) + { + switch (sub_name[0]) + { + case '.': + return value_sp->GetSubValue (exe_ctx, sub_name + 1, will_modify, error); + + case '{': + // Predicate matching for predicates like + // "{}" + // 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 exectable basename is "test" and arch is "x86_64" + if (sub_name[1]) + { + const char *predicate_start = sub_name + 1; + const char *predicate_end = strchr(predicate_start, '}'); + if (predicate_end) + { + std::string predicate(predicate_start, predicate_end); + if (PredicateMatches(exe_ctx, predicate.c_str())) + { + if (predicate_end[1]) + { + // Still more subvalue string to evaluate + return value_sp->GetSubValue (exe_ctx, predicate_end + 1, 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; +} + +Error +OptionValueProperties::SetSubValue (const ExecutionContext *exe_ctx, + VarSetOperationType op, + const char *name, + const char *value) +{ + Error error; + const bool will_modify = true; + lldb::OptionValueSP value_sp (GetSubValue (exe_ctx, name, will_modify, error)); + if (value_sp) + error = value_sp->SetValueFromCString(value, op); + else + { + if (error.AsCString() == NULL) + error.SetErrorStringWithFormat("invalid value path '%s'", name); + } + return error; +} + + +ConstString +OptionValueProperties::GetPropertyNameAtIndex (uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex(NULL, false, idx); + if (property) + return property->GetName(); + return ConstString(); + +} + +const char * +OptionValueProperties::GetPropertyDescriptionAtIndex (uint32_t idx) const +{ + const Property *property = GetPropertyAtIndex(NULL, false, idx); + if (property) + return property->GetDescription(); + return NULL; +} + +uint32_t +OptionValueProperties::GetPropertyIndex (const ConstString &name) const +{ + return m_name_to_index.Find (name.GetCString(), SIZE_MAX); +} + +const Property * +OptionValueProperties::GetProperty (const ExecutionContext *exe_ctx, bool will_modify, const ConstString &name) const +{ + return GetPropertyAtIndex (exe_ctx, will_modify, m_name_to_index.Find (name.GetCString(), 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 NULL; +} + +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 NULL; +} + +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 NULL; +} + +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 NULL; +} + +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; +} + + +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 NULL; +} + + +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 NULL; +} + +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 NULL; +} + +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; +} + +const char * +OptionValueProperties::GetPropertyAtIndexAsString (const ExecutionContext *exe_ctx, uint32_t idx, const char *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, const char *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 NULL; +} + + +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; iClear(); + return true; +} + + +Error +OptionValueProperties::SetValueFromCString (const char *value, VarSetOperationType op) +{ + Error 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::SetValueFromCString (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; iGetValue().get(); + assert (option_value); + const bool transparent_value = option_value->ValueIsTransparent (); + property->Dump (exe_ctx, + strm, + dump_mask); + if (!transparent_value) + strm.EOL(); + } + } +} + +Error +OptionValueProperties::DumpPropertyValue (const ExecutionContext *exe_ctx, + Stream &strm, + const char *property_path, + uint32_t dump_mask) +{ + Error 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 +{ + assert(!"this shouldn't happen"); +} + +const Property * +OptionValueProperties::GetPropertyAtPath (const ExecutionContext *exe_ctx, + bool will_modify, + const char *name) const +{ + const Property *property = NULL; + if (name && name[0]) + { + const char *sub_name = NULL; + ConstString key; + size_t key_len = ::strcspn (name, ".[{"); + + if (name[key_len]) + { + key.SetCStringWithLength (name, key_len); + sub_name = name + key_len; + } + else + key.SetCString (name); + + property = GetProperty (exe_ctx, will_modify, key); + if (sub_name && property) + { + if (sub_name[0] == '.') + { + OptionValueProperties *sub_properties = property->GetValue()->GetAsProperties(); + if (sub_properties) + return sub_properties->GetPropertyAtPath(exe_ctx, will_modify, sub_name + 1); + } + property = NULL; + } + } + return property; +} + +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(property->GetName().GetLength(), max_name_len); + } + for (size_t i=0; iDumpDescription (interpreter, strm, max_name_len, false); + } +} + +void +OptionValueProperties::Apropos (const char *keyword, std::vector &matching_properties) const +{ + const size_t num_properties = m_properties.size(); + StreamString strm; + for (size_t i=0; iGetValue()->GetAsProperties(); + if (properties) + { + properties->Apropos (keyword, matching_properties); + } + else + { + bool match = false; + const char *name = property->GetName().GetCString(); + if (name && ::strcasestr(name, keyword)) + match = true; + else + { + const char *desc = property->GetDescription(); + if (desc && ::strcasestr(desc, keyword)) + match = true; + } + if (match) + { + matching_properties.push_back (property); + } + } + } + } +} + +lldb::OptionValuePropertiesSP +OptionValueProperties::GetSubProperty (const ExecutionContext *exe_ctx, + const 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/source/Interpreter/OptionValueRegex.cpp b/source/Interpreter/OptionValueRegex.cpp new file mode 100644 index 00000000000..f1ba0ed04d6 --- /dev/null +++ b/source/Interpreter/OptionValueRegex.cpp @@ -0,0 +1,86 @@ +//===-- OptionValueRegex.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueRegex.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/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()) + { + const char *regex_text = m_regex.GetText(); + if (regex_text && regex_text[0]) + strm.Printf ("%s", regex_text); + } + else + { + + } + } +} + +Error +OptionValueRegex::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationInvalid: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (m_regex.Compile (value_cstr, m_regex.GetCompileFlags())) + { + m_value_was_set = true; + } + 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(), m_regex.GetCompileFlags())); +} diff --git a/source/Interpreter/OptionValueSInt64.cpp b/source/Interpreter/OptionValueSInt64.cpp new file mode 100644 index 00000000000..04bf9306ade --- /dev/null +++ b/source/Interpreter/OptionValueSInt64.cpp @@ -0,0 +1,89 @@ +//===-- OptionValueSInt64.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueSInt64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.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); + } +} + +Error +OptionValueSInt64::SetValueFromCString (const char *value_cstr, VarSetOperationType op) +{ + //printf ("%p: SetValueFromCString (s=\"%s\", op=%i)\n", this, value_cstr, op); + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + int64_t value = Args::StringToSInt64 (value_cstr, 0, 0, &success); + if (success) + { + if (value >= m_min_value && value <= m_max_value) + { + m_value_was_set = true; + m_current_value = value; + } + 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_cstr); + } + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueSInt64::DeepCopy () const +{ + return OptionValueSP(new OptionValueSInt64(*this)); +} diff --git a/source/Interpreter/OptionValueString.cpp b/source/Interpreter/OptionValueString.cpp new file mode 100644 index 00000000000..df047bd9899 --- /dev/null +++ b/source/Interpreter/OptionValueString.cpp @@ -0,0 +1,186 @@ +//===-- OptionValueString.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueString.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.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()); + } + } + } +} + +Error +OptionValueString::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + + std::string value_str_no_quotes; + if (value_cstr) + { + switch (value_cstr[0]) + { + case '"': + case '\'': + { + size_t len = strlen(value_cstr); + if (len <= 1 || value_cstr[len-1] != value_cstr[0]) + { + error.SetErrorString("mismatched quotes"); + return error; + } + value_str_no_quotes.assign (value_cstr + 1, len - 2); + value_cstr = value_str_no_quotes.c_str(); + } + break; + } + } + + switch (op) + { + case eVarSetOperationInvalid: + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + if (m_validator) + { + error = m_validator(value_cstr,m_validator_baton); + if (error.Fail()) + return error; + } + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + + case eVarSetOperationAppend: + { + std::string new_value(m_current_value); + if (value_cstr && value_cstr[0]) + { + if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + { + std::string str; + Args::EncodeEscapeSequences (value_cstr, str); + new_value.append(str); + } + else + new_value.append(value_cstr); + } + if (m_validator) + { + error = m_validator(new_value.c_str(),m_validator_baton); + if (error.Fail()) + return error; + } + m_current_value.assign(new_value); + } + break; + + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + if (m_validator) + { + error = m_validator(value_cstr,m_validator_baton); + if (error.Fail()) + return error; + } + m_value_was_set = true; + if (m_options.Test (eOptionEncodeCharacterEscapeSequences)) + { + Args::EncodeEscapeSequences (value_cstr, m_current_value); + } + else + { + SetCurrentValue (value_cstr); + } + break; + } + return error; +} + + +lldb::OptionValueSP +OptionValueString::DeepCopy () const +{ + return OptionValueSP(new OptionValueString(*this)); +} + +Error +OptionValueString::SetCurrentValue (const char *value) +{ + if (m_validator) + { + Error error(m_validator(value,m_validator_baton)); + if (error.Fail()) + return error; + } + if (value && value[0]) + m_current_value.assign (value); + else + m_current_value.clear(); + return Error(); +} + +Error +OptionValueString::AppendToCurrentValue (const char *value) +{ + if (value && value[0]) + { + if (m_validator) + { + std::string new_value(m_current_value); + new_value.append(value); + Error 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 Error(); +} diff --git a/source/Interpreter/OptionValueUInt64.cpp b/source/Interpreter/OptionValueUInt64.cpp new file mode 100644 index 00000000000..56b3a1c7470 --- /dev/null +++ b/source/Interpreter/OptionValueUInt64.cpp @@ -0,0 +1,89 @@ +//===-- OptionValueUInt64.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/OptionValueUInt64.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/Args.h" + +using namespace lldb; +using namespace lldb_private; + +lldb::OptionValueSP +OptionValueUInt64::Create (const char *value_cstr, Error &error) +{ + lldb::OptionValueSP value_sp (new OptionValueUInt64()); + error = value_sp->SetValueFromCString (value_cstr); + 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); + } +} + +Error +OptionValueUInt64::SetValueFromCString (const char *value_cstr, VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear (); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + bool success = false; + uint64_t value = Args::StringToUInt64 (value_cstr, 0, 0, &success); + if (success) + { + m_value_was_set = true; + m_current_value = value; + } + else + { + error.SetErrorStringWithFormat ("invalid uint64_t string value: '%s'", value_cstr); + } + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueUInt64::DeepCopy () const +{ + return OptionValueSP(new OptionValueUInt64(*this)); +} + diff --git a/source/Interpreter/OptionValueUUID.cpp b/source/Interpreter/OptionValueUUID.cpp new file mode 100644 index 00000000000..340f1e5e998 --- /dev/null +++ b/source/Interpreter/OptionValueUUID.cpp @@ -0,0 +1,123 @@ +//===-- OptionValueUUID.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/OptionValueUUID.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/CommandInterpreter.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); + } +} + +Error +OptionValueUUID::SetValueFromCString (const char *value_cstr, + VarSetOperationType op) +{ + Error error; + switch (op) + { + case eVarSetOperationClear: + Clear(); + break; + + case eVarSetOperationReplace: + case eVarSetOperationAssign: + { + if (m_uuid.SetFromCString(value_cstr) == 0) + error.SetErrorStringWithFormat ("invalid uuid string value '%s'", value_cstr); + else + m_value_was_set = true; + } + break; + + case eVarSetOperationInsertBefore: + case eVarSetOperationInsertAfter: + case eVarSetOperationRemove: + case eVarSetOperationAppend: + case eVarSetOperationInvalid: + error = OptionValue::SetValueFromCString (value_cstr, op); + break; + } + return error; +} + +lldb::OptionValueSP +OptionValueUUID::DeepCopy () const +{ + return OptionValueSP(new OptionValueUUID(*this)); +} + +size_t +OptionValueUUID::AutoComplete (CommandInterpreter &interpreter, + const char *s, + int match_start_point, + int max_return_elements, + bool &word_complete, + StringList &matches) +{ + word_complete = false; + matches.Clear(); + ExecutionContext exe_ctx(interpreter.GetExecutionContext()); + Target *target = exe_ctx.GetTargetPtr(); + if (target) + { + const size_t num_modules = target->GetImages().GetSize(); + if (num_modules > 0) + { + UUID::ValueType uuid_bytes; + const size_t num_bytes_decoded = UUID::DecodeUUIDBytesFromCString(s, uuid_bytes, NULL); + for (size_t i=0; iGetImages().GetModuleAtIndex(i)); + if (module_sp) + { + const UUID &module_uuid = module_sp->GetUUID(); + if (module_uuid.IsValid()) + { + bool add_uuid = false; + if (num_bytes_decoded == 0) + add_uuid = true; + else + add_uuid = ::memcmp(module_uuid.GetBytes(), uuid_bytes, num_bytes_decoded) == 0; + if (add_uuid) + { + std::string uuid_str; + uuid_str = module_uuid.GetAsString(); + if (!uuid_str.empty()) + matches.AppendString(uuid_str.c_str()); + } + } + } + } + } + } + return matches.GetSize(); +} + diff --git a/source/Interpreter/Options.cpp b/source/Interpreter/Options.cpp new file mode 100644 index 00000000000..293d7535663 --- /dev/null +++ b/source/Interpreter/Options.cpp @@ -0,0 +1,1077 @@ +//===-- Options.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/Options.h" + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/CommandObject.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/CommandCompletions.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Target/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//------------------------------------------------------------------------- +// Options +//------------------------------------------------------------------------- +Options::Options (CommandInterpreter &interpreter) : + m_interpreter (interpreter), + m_getopt_table () +{ + BuildValidOptionSets(); +} + +Options::~Options () +{ +} + +void +Options::NotifyOptionParsingStarting () +{ + m_seen_options.clear(); + // Let the subclass reset its option values + OptionParsingStarting (); +} + +Error +Options::NotifyOptionParsingFinished () +{ + return OptionParsingFinished (); +} + +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; + + const OptionDefinition *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 (int i = 0; i < num_options; i++) + { + uint32_t this_usage_mask = opt_defs[i].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 (int i = 0; i < num_options; ++i) + { + for (uint32_t j = 0; j < num_option_sets; j++) + { + if (opt_defs[i].usage_mask & 1 << j) + { + if (opt_defs[i].required) + m_required_options[j].insert(opt_defs[i].short_option); + else + m_optional_options[j].insert(opt_defs[i].short_option); + } + } + } + } +} + +uint32_t +Options::NumCommandOptions () +{ + const OptionDefinition *opt_defs = GetDefinitions (); + if (opt_defs == NULL) + return 0; + + int i = 0; + + if (opt_defs != NULL) + { + while (opt_defs[i].long_option != NULL) + ++i; + } + + return i; +} + +struct option * +Options::GetLongOptions () +{ + // Check to see if this has already been done. + if (m_getopt_table.empty()) + { + // Check to see if there are any options. + const uint32_t num_options = NumCommandOptions(); + if (num_options == 0) + return NULL; + + uint32_t i; + const OptionDefinition *opt_defs = GetDefinitions(); + + std::map option_seen; + + m_getopt_table.resize(num_options + 1); + for (i = 0; i < num_options; ++i) + { + const int short_opt = opt_defs[i].short_option; + + m_getopt_table[i].name = opt_defs[i].long_option; + m_getopt_table[i].has_arg = opt_defs[i].option_has_arg; + m_getopt_table[i].flag = NULL; + 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::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", + i, + opt_defs[i].long_option, + short_opt, + pos->second, + m_getopt_table[pos->second].name, + opt_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", + i, + opt_defs[i].long_option, + short_opt, + pos->second, + m_getopt_table[pos->second].name, + opt_defs[i].long_option); + } + } + + //getopt_long_only requires a NULL final entry in the table: + + m_getopt_table[i].name = NULL; + m_getopt_table[i].has_arg = 0; + m_getopt_table[i].flag = NULL; + m_getopt_table[i].val = 0; + } + + if (m_getopt_table.empty()) + return NULL; + + 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 char *text, + uint32_t output_max_columns +) +{ + int len = strlen (text); + + // Will it all fit on one line? + + if ((len + strm.GetIndentLevel()) < output_max_columns) + { + // Output it as a single line. + strm.Indent (text); + 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 = strlen (text); + 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) && (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 + && text[end] != ' ' && text[end] != '\t' && 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(text + start, sub_len); + start = end + 1; + } + strm.EOL(); + } +} + +bool +Options::SupportsLongOption (const char *long_option) +{ + if (long_option && long_option[0]) + { + const OptionDefinition *opt_defs = GetDefinitions (); + if (opt_defs) + { + const char *long_option_name = long_option; + if (long_option[0] == '-' && long_option[1] == '-') + long_option_name += 2; + + for (uint32_t i = 0; opt_defs[i].long_option; ++i) + { + if (strcmp(opt_defs[i].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 no_argument: + break; + case required_argument: + strm.Printf (" <%s>", CommandObject::GetArgumentName (opt_def.argument_type)); + break; + + case optional_argument: + 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 +) +{ + const uint32_t screen_width = m_interpreter.GetDebugger().GetTerminalWidth(); + + const OptionDefinition *opt_defs = GetDefinitions(); + const uint32_t save_indent_level = strm.GetIndentLevel(); + const char *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. [options-for-level-0] + // [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; + + 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 options; + std::set::const_iterator options_pos, options_end; + for (i = 0; i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask && isprint8(opt_defs[i].short_option)) + { + // Add current option to the end of out_stream. + + if (opt_defs[i].required == true && + opt_defs[i].option_has_arg == no_argument) + { + options.insert (opt_defs[i].short_option); + } + } + } + + if (options.empty() == false) + { + // 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; + } + } + + for (i = 0, options.clear(); i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask && isprint8(opt_defs[i].short_option)) + { + // Add current option to the end of out_stream. + + if (opt_defs[i].required == false && + opt_defs[i].option_has_arg == no_argument) + { + options.insert (opt_defs[i].short_option); + } + } + } + + if (options.empty() == false) + { + // 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 (i = 0; i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask && isprint8(opt_defs[i].short_option)) + { + if (opt_defs[i].required && opt_defs[i].option_has_arg != no_argument) + PrintOption (opt_defs[i], eDisplayBestOption, " ", NULL, true, strm); + } + } + + // Now go through again, and this time only print the optional options. + + for (i = 0; i < num_options; ++i) + { + if (opt_defs[i].usage_mask & opt_set_mask) + { + // Add current option to the end of out_stream. + + if (!opt_defs[i].required && opt_defs[i].option_has_arg != no_argument) + PrintOption (opt_defs[i], eDisplayBestOption, " ", NULL, true, strm); + } + } + + if (args_str.GetSize() > 0) + { + if (cmd->WantsRawCommandString()) + strm.Printf(" --"); + + strm.Printf (" %s", args_str.GetData()); + } + } + + if (cmd && + cmd->WantsRawCommandString() && + arguments_str.GetSize() > 0) + { + strm.PutChar('\n'); + strm.Indent(name); + strm.Printf(" %s", arguments_str.GetData()); + } + + strm.Printf ("\n\n"); + + // Now print out all the detailed information about the various options: long form, short form and help text: + // --long_name ( -short ) + // 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 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. + + for (i = 0; i < num_options; ++i) + options_seen.insert(std::make_pair(opt_defs[i].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, NULL, NULL, false, strm); + PrintOption (opt_defs[i], eDisplayLongOption, " ( ", " )", false, strm); + } + else + { + // Short option is not printable, just print long option + PrintOption (opt_defs[i], eDisplayLongOption, NULL, NULL, false, strm); + } + strm.EOL(); + + strm.IndentMore (5); + + if (opt_defs[i].usage_text) + OutputFormattedUsageText (strm, + opt_defs[i].usage_text, + screen_width); + if (opt_defs[i].enum_values != NULL) + { + strm.Indent (); + strm.Printf("Values: "); + for (int k = 0; opt_defs[i].enum_values[k].string_value != NULL; k++) + { + if (k == 0) + strm.Printf("%s", opt_defs[i].enum_values[k].string_value); + else + strm.Printf(" | %s", opt_defs[i].enum_values[k].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 +( + Args &input, + OptionElementVector &opt_element_vector, + int cursor_index, + int char_pos, + int match_start_point, + int max_return_elements, + bool &word_complete, + lldb_private::StringList &matches +) +{ + word_complete = 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. + + const OptionDefinition *opt_defs = GetDefinitions(); + + std::string cur_opt_std_str (input.GetArgumentAtIndex(cursor_index)); + cur_opt_std_str.erase(char_pos); + 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 == cursor_index) + { + // 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 (int j = 0 ; opt_defs[j].short_option != 0 ; j++) + { + opt_str[1] = opt_defs[j].short_option; + matches.AppendString (opt_str); + } + return true; + } + else if (opt_defs_index == OptionArgElement::eBareDoubleDash) + { + std::string full_name ("--"); + for (int j = 0 ; opt_defs[j].short_option != 0 ; j++) + { + full_name.erase(full_name.begin() + 2, full_name.end()); + full_name.append (opt_defs[j].long_option); + matches.AppendString (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); + matches.AppendString(full_name.c_str()); + return true; + } + else + { + matches.AppendString(input.GetArgumentAtIndex(cursor_index)); + 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 (int j = 0 ; opt_defs[j].short_option != 0 ; j++) + { + if (strstr(opt_defs[j].long_option, cur_opt_str + 2) == opt_defs[j].long_option) + { + std::string full_name ("--"); + full_name.append (opt_defs[j].long_option); + // The options definitions table has duplicates because of the + // way the grouping information is stored, so only add once. + bool duplicate = false; + for (size_t k = 0; k < matches.GetSize(); k++) + { + if (matches.GetStringAtIndex(k) == full_name) + { + duplicate = true; + break; + } + } + if (!duplicate) + matches.AppendString(full_name.c_str()); + } + } + } + return true; + } + + + } + else if (opt_arg_pos == cursor_index) + { + // Okay the cursor is on the completion of an argument. + // See if it has a completion, otherwise return no matches. + + if (opt_defs_index != -1) + { + HandleOptionArgumentCompletion (input, + cursor_index, + strlen (input.GetArgumentAtIndex(cursor_index)), + opt_element_vector, + i, + match_start_point, + max_return_elements, + word_complete, + matches); + return true; + } + else + { + // No completion callback means no completions... + return true; + } + + } + else + { + // Not the last element, keep going. + continue; + } + } + return false; +} + +bool +Options::HandleOptionArgumentCompletion +( + Args &input, + int cursor_index, + int char_pos, + OptionElementVector &opt_element_vector, + int opt_element_index, + int match_start_point, + int max_return_elements, + bool &word_complete, + lldb_private::StringList &matches +) +{ + const OptionDefinition *opt_defs = GetDefinitions(); + std::unique_ptr filter_ap; + + 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: + + OptionEnumValueElement *enum_values = opt_defs[opt_defs_index].enum_values; + if (enum_values != NULL) + { + bool return_value = false; + std::string match_string(input.GetArgumentAtIndex (opt_arg_pos), input.GetArgumentAtIndex (opt_arg_pos) + char_pos); + for (int i = 0; enum_values[i].string_value != NULL; i++) + { + if (strstr(enum_values[i].string_value, match_string.c_str()) == enum_values[i].string_value) + { + matches.AppendString (enum_values[i].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) + { + 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; + 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 (strcmp(cur_opt_name, "shlib") == 0 && cur_arg_pos != -1) + { + const char *module_name = input.GetArgumentAtIndex(cur_arg_pos); + if (module_name) + { + FileSpec module_spec(module_name, false); + lldb::TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget(); + // Search filters require a target... + if (target_sp) + filter_ap.reset (new SearchFilterByModule (target_sp, module_spec)); + } + break; + } + } + } + + return CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter, + completion_mask, + input.GetArgumentAtIndex (opt_arg_pos), + match_start_point, + max_return_elements, + filter_ap.get(), + word_complete, + matches); + +} + + +void +OptionGroupOptions::Append (OptionGroup* group) +{ + const OptionDefinition* group_option_defs = group->GetDefinitions (); + const uint32_t group_option_count = group->GetNumDefinitions(); + for (uint32_t i=0; iGetDefinitions (); + const uint32_t group_option_count = group->GetNumDefinitions(); + for (uint32_t i=0; iSetOptionValue (m_interpreter, + m_option_infos[option_idx].option_index, + option_value); + + } + else + { + error.SetErrorString ("invalid option index"); // Shouldn't happen... + } + return error; +} + +void +OptionGroupOptions::OptionParsingStarting () +{ + std::set 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 (m_interpreter); + group_set.insert(group); + } + } +} +Error +OptionGroupOptions::OptionParsingFinished () +{ + std::set group_set; + Error 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 (m_interpreter); + group_set.insert(group); + if (error.Fail()) + return error; + } + } + return error; +} diff --git a/source/Interpreter/Property.cpp b/source/Interpreter/Property.cpp new file mode 100644 index 00000000000..e5cf63ada88 --- /dev/null +++ b/source/Interpreter/Property.cpp @@ -0,0 +1,275 @@ +//===-- Property.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/Property.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/UserSettingsController.h" +#include "lldb/Interpreter/Args.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValues.h" + +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.reset (new OptionValueArch(definition.default_cstr_value)); + break; + + case OptionValue::eTypeArgs: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp.reset (new OptionValueArgs()); + break; + + case OptionValue::eTypeArray: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp.reset (new 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.reset (new OptionValueBoolean(Args::StringToBoolean (definition.default_cstr_value, false, NULL))); + else + m_value_sp.reset (new OptionValueBoolean(definition.default_uint_value != 0)); + break; + + case OptionValue::eTypeDictionary: + // "definition.default_uint_value" is always a OptionValue::Type + m_value_sp.reset (new 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->SetValueFromCString(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 SetValueFromCString() 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 + m_value_sp.reset (new OptionValueFileSpec(FileSpec(definition.default_cstr_value, definition.default_uint_value != 0))); + break; + + case OptionValue::eTypeFileSpecList: + // "definition.default_uint_value" is not used for a OptionValue::eTypeFileSpecList + m_value_sp.reset (new 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) + Args::StringToFormat (definition.default_cstr_value, new_format, NULL); + else + new_format = (Format)definition.default_uint_value; + m_value_sp.reset (new OptionValueFormat(new_format)); + } + break; + + case OptionValue::eTypePathMap: + // "definition.default_uint_value" tells us if notifications should occur for + // path mappings + m_value_sp.reset (new 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.reset (new OptionValueRegex(definition.default_cstr_value, definition.default_uint_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.reset (new OptionValueSInt64(definition.default_cstr_value ? Args::StringToSInt64 (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.reset (new OptionValueUInt64(definition.default_cstr_value ? Args::StringToUInt64 (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.SetFromCString (definition.default_cstr_value); + m_value_sp.reset (new 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 (const ConstString &name, + const 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 transparent = m_value_sp->ValueIsTransparent (); + if (dump_desc || !transparent) + { + if ((dump_mask & OptionValue::eDumpOptionName) && m_name) + { + DumpQualifiedName(strm); + if (dump_mask & ~OptionValue::eDumpOptionName) + strm.PutChar(' '); + } + } + if (dump_desc) + { + const char *desc = GetDescription(); + if (desc) + strm.Printf ("-- %s", 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) + { + const char *desc = GetDescription(); + + if (desc) + { + 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.GetString().c_str()); + sub_properties->DumpAllDescriptions(interpreter, strm); + } + else + { + if (desc) + { + if (display_qualified_name) + { + StreamString qualified_name; + DumpQualifiedName(qualified_name); + interpreter.OutputFormattedHelpText (strm, + qualified_name.GetString().c_str(), + "--", + desc, + output_width); + } + else + { + interpreter.OutputFormattedHelpText (strm, + m_name.GetCString(), + "--", + desc, + output_width); + } + } + } + } + } +} + diff --git a/source/Interpreter/PythonDataObjects.cpp b/source/Interpreter/PythonDataObjects.cpp new file mode 100644 index 00000000000..2a1f348e14b --- /dev/null +++ b/source/Interpreter/PythonDataObjects.cpp @@ -0,0 +1,430 @@ +//===-- PythonDataObjects.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// In order to guarantee correct working with Python, Python.h *MUST* be +// the *FIRST* header file included here. +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#include + +#include "lldb/Core/Stream.h" +#include "lldb/Host/File.h" +#include "lldb/Interpreter/PythonDataObjects.h" +#include "lldb/Interpreter/ScriptInterpreter.h" + +using namespace lldb_private; +using namespace lldb; + +//---------------------------------------------------------------------- +// PythonObject +//---------------------------------------------------------------------- +PythonObject::PythonObject (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + m_py_obj (NULL) +{ + if (script_object_sp) + Reset ((PyObject *)script_object_sp->GetObject()); +} + +void +PythonObject::Dump (Stream &strm) const +{ + if (m_py_obj) + { + FILE *file = ::tmpfile(); + if (file) + { + ::PyObject_Print (m_py_obj, file, 0); + const long length = ftell (file); + if (length) + { + ::rewind(file); + std::vector 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"); +} + +PythonString +PythonObject::Repr () +{ + if (!m_py_obj) + return PythonString (); + PyObject *repr = PyObject_Repr(m_py_obj); + if (!repr) + return PythonString (); + return PythonString(repr); +} + +PythonString +PythonObject::Str () +{ + if (!m_py_obj) + return PythonString (); + PyObject *str = PyObject_Str(m_py_obj); + if (!str) + return PythonString (); + return PythonString(str); +} + +//---------------------------------------------------------------------- +// PythonString +//---------------------------------------------------------------------- + +PythonString::PythonString (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + +PythonString::PythonString (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonString::PythonString (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonString::PythonString (const char* string) : + PythonObject(PyString_FromString(string)) +{ +} + +PythonString::PythonString () : + PythonObject() +{ +} + +PythonString::~PythonString () +{ +} + +bool +PythonString::Reset (PyObject *py_obj) +{ + if (py_obj && PyString_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +const char* +PythonString::GetString() const +{ + if (m_py_obj) + return PyString_AsString(m_py_obj); + return NULL; +} + +size_t +PythonString::GetSize() const +{ + if (m_py_obj) + return PyString_Size(m_py_obj); + return 0; +} + +void +PythonString::SetString (const char* string) +{ + PythonObject::Reset(PyString_FromString(string)); +} + +//---------------------------------------------------------------------- +// PythonInteger +//---------------------------------------------------------------------- + +PythonInteger::PythonInteger (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + +PythonInteger::PythonInteger (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonInteger::PythonInteger (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonInteger::PythonInteger (int64_t value) : + PythonObject(PyInt_FromLong(value)) +{ +} + + +PythonInteger::~PythonInteger () +{ +} + +bool +PythonInteger::Reset (PyObject *py_obj) +{ + if (py_obj && PyInt_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +int64_t +PythonInteger::GetInteger() +{ + if (m_py_obj) + return PyInt_AsLong(m_py_obj); + else + return UINT64_MAX; +} + +void +PythonInteger::SetInteger (int64_t value) +{ + PythonObject::Reset(PyInt_FromLong(value)); +} + +//---------------------------------------------------------------------- +// PythonList +//---------------------------------------------------------------------- + +PythonList::PythonList () : + PythonObject(PyList_New(0)) +{ +} + +PythonList::PythonList (uint32_t count) : + PythonObject(PyList_New(count)) +{ +} + +PythonList::PythonList (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + + +PythonList::PythonList (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonList::PythonList (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonList::~PythonList () +{ +} + +bool +PythonList::Reset (PyObject *py_obj) +{ + if (py_obj && PyList_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +uint32_t +PythonList::GetSize() +{ + if (m_py_obj) + return PyList_GET_SIZE(m_py_obj); + return 0; +} + +PythonObject +PythonList::GetItemAtIndex (uint32_t index) +{ + if (m_py_obj) + return PythonObject(PyList_GetItem(m_py_obj, index)); + return NULL; +} + +void +PythonList::SetItemAtIndex (uint32_t index, const PythonObject & object) +{ + if (m_py_obj && object) + PyList_SetItem(m_py_obj, index, object.GetPythonObject()); +} + +void +PythonList::AppendItem (const PythonObject &object) +{ + if (m_py_obj && object) + PyList_Append(m_py_obj, object.GetPythonObject()); +} + +//---------------------------------------------------------------------- +// PythonDictionary +//---------------------------------------------------------------------- + +PythonDictionary::PythonDictionary () : + PythonObject(PyDict_New()) +{ +} + +PythonDictionary::PythonDictionary (PyObject *py_obj) : + PythonObject(py_obj) +{ +} + + +PythonDictionary::PythonDictionary (const PythonObject &object) : + PythonObject(object.GetPythonObject()) +{ +} + +PythonDictionary::PythonDictionary (const lldb::ScriptInterpreterObjectSP &script_object_sp) : + PythonObject (script_object_sp) +{ +} + +PythonDictionary::~PythonDictionary () +{ +} + +bool +PythonDictionary::Reset (PyObject *py_obj) +{ + if (py_obj && PyDict_Check(py_obj)) + return PythonObject::Reset(py_obj); + + PythonObject::Reset(NULL); + return py_obj == NULL; +} + +uint32_t +PythonDictionary::GetSize() +{ + if (m_py_obj) + return PyDict_Size(m_py_obj); + return 0; +} + +PythonObject +PythonDictionary::GetItemForKey (const char *key) const +{ + if (key && key[0]) + { + PythonString python_key(key); + return GetItemForKey(python_key); + } + return NULL; +} + + +PythonObject +PythonDictionary::GetItemForKey (const PythonString &key) const +{ + if (m_py_obj && key) + return PythonObject(PyDict_GetItem(m_py_obj, key.GetPythonObject())); + return PythonObject(); +} + + +const char * +PythonDictionary::GetItemForKeyAsString (const PythonString &key, const char *fail_value) const +{ + if (m_py_obj && key) + { + PyObject *py_obj = PyDict_GetItem(m_py_obj, key.GetPythonObject()); + if (py_obj && PyString_Check(py_obj)) + return PyString_AsString(py_obj); + } + return fail_value; +} + +int64_t +PythonDictionary::GetItemForKeyAsInteger (const PythonString &key, int64_t fail_value) const +{ + if (m_py_obj && key) + { + PyObject *py_obj = PyDict_GetItem(m_py_obj, key.GetPythonObject()); + if (py_obj) + { + if (PyInt_Check(py_obj)) + return PyInt_AsLong(py_obj); + + if (PyLong_Check(py_obj)) + return PyLong_AsLong(py_obj); + } + } + return fail_value; +} + +PythonList +PythonDictionary::GetKeys () const +{ + if (m_py_obj) + return PythonList(PyDict_Keys(m_py_obj)); + return PythonList(); +} + +PythonString +PythonDictionary::GetKeyAtPosition (uint32_t pos) const +{ + PyObject *key, *value; + Py_ssize_t pos_iter = 0; + + if (m_py_obj) + { + while (PyDict_Next(m_py_obj, &pos_iter, &key, &value)) + { + if (pos-- == 0) + return PythonString(key); + } + } + return PythonString(); +} + +PythonObject +PythonDictionary::GetValueAtPosition (uint32_t pos) const +{ + PyObject *key, *value; + Py_ssize_t pos_iter = 0; + + if (!m_py_obj) + return NULL; + + while (PyDict_Next(m_py_obj, &pos_iter, &key, &value)) { + if (pos-- == 0) + return PythonObject(value); + } + return PythonObject(); +} + +void +PythonDictionary::SetItemForKey (const PythonString &key, const PythonObject &value) +{ + if (m_py_obj && key && value) + PyDict_SetItem(m_py_obj, key.GetPythonObject(), value.GetPythonObject()); +} + +#endif diff --git a/source/Interpreter/ScriptInterpreter.cpp b/source/Interpreter/ScriptInterpreter.cpp new file mode 100644 index 00000000000..67314731df2 --- /dev/null +++ b/source/Interpreter/ScriptInterpreter.cpp @@ -0,0 +1,105 @@ +//===-- ScriptInterpreter.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/ScriptInterpreter.h" + +#include +#include +#include + +#include "lldb/Core/Error.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Interpreter/ScriptInterpreterPython.h" +#include "lldb/Utility/PseudoTerminal.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreter::ScriptInterpreter (CommandInterpreter &interpreter, lldb::ScriptLanguage script_lang) : + m_interpreter (interpreter), + m_script_lang (script_lang) +{ +} + +ScriptInterpreter::~ScriptInterpreter () +{ +} + +CommandInterpreter & +ScriptInterpreter::GetCommandInterpreter () +{ + return m_interpreter; +} + +void +ScriptInterpreter::CollectDataForBreakpointCommandCallback +( + BreakpointOptions *bp_options, + 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; + } + + return return_value; +} + +std::unique_ptr +ScriptInterpreter::AcquireInterpreterLock () +{ + return std::unique_ptr(new ScriptInterpreterLocker()); +} + +void +ScriptInterpreter::InitializeInterpreter (SWIGInitCallback python_swig_init_callback) +{ +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreterPython::InitializeInterpreter (python_swig_init_callback); +#endif // #ifndef LLDB_DISABLE_PYTHON +} + +void +ScriptInterpreter::TerminateInterpreter () +{ +#ifndef LLDB_DISABLE_PYTHON + ScriptInterpreterPython::TerminateInterpreter (); +#endif // #ifndef LLDB_DISABLE_PYTHON +} + diff --git a/source/Interpreter/ScriptInterpreterNone.cpp b/source/Interpreter/ScriptInterpreterNone.cpp new file mode 100644 index 00000000000..6a4411494c4 --- /dev/null +++ b/source/Interpreter/ScriptInterpreterNone.cpp @@ -0,0 +1,43 @@ +//===-- ScriptInterpreterNone.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/lldb-python.h" + +#include "lldb/Interpreter/ScriptInterpreterNone.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StringList.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Interpreter/CommandInterpreter.h" + +using namespace lldb; +using namespace lldb_private; + +ScriptInterpreterNone::ScriptInterpreterNone (CommandInterpreter &interpreter) : + ScriptInterpreter (interpreter, eScriptLanguageNone) +{ +} + +ScriptInterpreterNone::~ScriptInterpreterNone () +{ +} + +bool +ScriptInterpreterNone::ExecuteOneLine (const char *command, CommandReturnObject *, const ExecuteScriptOptions&) +{ + m_interpreter.GetDebugger().GetErrorStream().PutCString ("error: there is no embedded script interpreter in this mode.\n"); + return false; +} + +void +ScriptInterpreterNone::ExecuteInterpreterLoop () +{ + m_interpreter.GetDebugger().GetErrorStream().PutCString ("error: there is no embedded script interpreter in this mode.\n"); +} + + diff --git a/source/Interpreter/ScriptInterpreterPython.cpp b/source/Interpreter/ScriptInterpreterPython.cpp new file mode 100644 index 00000000000..9d9b8d93fb4 --- /dev/null +++ b/source/Interpreter/ScriptInterpreterPython.cpp @@ -0,0 +1,3166 @@ +//===-- ScriptInterpreterPython.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// In order to guarantee correct working with Python, Python.h *MUST* be +// the *FIRST* header file included here. +#ifdef LLDB_DISABLE_PYTHON + +// Python is disabled in this build + +#else + +#if defined (__APPLE__) +#include +#else +#include +#endif + +#include "lldb/Interpreter/ScriptInterpreterPython.h" + +#include +#include + +#include + +#include "lldb/API/SBValue.h" +#include "lldb/Breakpoint/BreakpointLocation.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Breakpoint/WatchpointOptions.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Timer.h" +#include "lldb/Host/Host.h" +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + + +static ScriptInterpreter::SWIGInitCallback g_swig_init_callback = NULL; +static ScriptInterpreter::SWIGBreakpointCallbackFunction g_swig_breakpoint_callback = NULL; +static ScriptInterpreter::SWIGWatchpointCallbackFunction g_swig_watchpoint_callback = NULL; +static ScriptInterpreter::SWIGPythonTypeScriptCallbackFunction g_swig_typescript_callback = NULL; +static ScriptInterpreter::SWIGPythonCreateSyntheticProvider g_swig_synthetic_script = NULL; +static ScriptInterpreter::SWIGPythonCalculateNumChildren g_swig_calc_children = NULL; +static ScriptInterpreter::SWIGPythonGetChildAtIndex g_swig_get_child_index = NULL; +static ScriptInterpreter::SWIGPythonGetIndexOfChildWithName g_swig_get_index_child = NULL; +static ScriptInterpreter::SWIGPythonCastPyObjectToSBValue g_swig_cast_to_sbvalue = NULL; +static ScriptInterpreter::SWIGPythonUpdateSynthProviderInstance g_swig_update_provider = NULL; +static ScriptInterpreter::SWIGPythonMightHaveChildrenSynthProviderInstance g_swig_mighthavechildren_provider = NULL; +static ScriptInterpreter::SWIGPythonCallCommand g_swig_call_command = NULL; +static ScriptInterpreter::SWIGPythonCallModuleInit g_swig_call_module_init = NULL; +static ScriptInterpreter::SWIGPythonCreateOSPlugin g_swig_create_os_plugin = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Process g_swig_run_script_keyword_process = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Thread g_swig_run_script_keyword_thread = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Target g_swig_run_script_keyword_target = NULL; +static ScriptInterpreter::SWIGPythonScriptKeyword_Frame g_swig_run_script_keyword_frame = NULL; + +// these are the Pythonic implementations of the required callbacks +// these are scripting-language specific, which is why they belong here +// we still need to use function pointers to them instead of relying +// on linkage-time resolution because the SWIG stuff and this file +// get built at different times +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, + std::string& retval); + +extern "C" void* +LLDBSwigPythonCreateSyntheticProvider (const char *python_class_name, + const char *session_dictionary_name, + const lldb::ValueObjectSP& valobj_sp); + + +extern "C" uint32_t +LLDBSwigPython_CalculateNumChildren (void *implementor); + +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 "C" bool +LLDBSwigPython_UpdateSynthProviderInstance (void* implementor); + +extern "C" bool +LLDBSwigPython_MightHaveChildrenSynthProviderInstance (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); + +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" 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); + +static int +_check_and_flush (FILE *stream) +{ + int prev_fail = ferror (stream); + return fflush (stream) || prev_fail ? EOF : 0; +} + +ScriptInterpreterPython::Locker::Locker (ScriptInterpreterPython *py_interpreter, + uint16_t on_entry, + uint16_t on_leave, + FILE* wait_msg_handle) : + ScriptInterpreterLocker (), + m_teardown_session( (on_leave & TearDownSession) == TearDownSession ), + m_python_interpreter(py_interpreter), + m_tmp_fh(wait_msg_handle) +{ + if (m_python_interpreter && !m_tmp_fh) + m_tmp_fh = (m_python_interpreter->m_dbg_stdout ? m_python_interpreter->m_dbg_stdout : stdout); + + DoAcquireLock(); + if ((on_entry & InitSession) == InitSession) + { + if (DoInitSession((on_entry & InitGlobals) == InitGlobals) == false) + { + // Don't teardown the session if we didn't init it. + m_teardown_session = false; + } + } +} + +bool +ScriptInterpreterPython::Locker::DoAcquireLock() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); + m_GILState = PyGILState_Ensure(); + if (log) + log->Printf("Ensured PyGILState. Previous state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : ""); + return true; +} + +bool +ScriptInterpreterPython::Locker::DoInitSession(bool init_lldb_globals) +{ + if (!m_python_interpreter) + return false; + return m_python_interpreter->EnterSession (init_lldb_globals); +} + +bool +ScriptInterpreterPython::Locker::DoFreeLock() +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); + if (log) + log->Printf("Releasing PyGILState. Returning to state = %slocked\n", m_GILState == PyGILState_UNLOCKED ? "un" : ""); + PyGILState_Release(m_GILState); + return true; +} + +bool +ScriptInterpreterPython::Locker::DoTearDownSession() +{ + if (!m_python_interpreter) + return false; + m_python_interpreter->LeaveSession (); + return true; +} + +ScriptInterpreterPython::Locker::~Locker() +{ + if (m_teardown_session) + DoTearDownSession(); + DoFreeLock(); +} + +ScriptInterpreterPython::PythonInputReaderManager::PythonInputReaderManager (ScriptInterpreterPython *interpreter) : +m_interpreter(interpreter), +m_debugger_sp(), +m_reader_sp(), +m_error(false) +{ + if (m_interpreter == NULL) + { + m_error = true; + return; + } + + m_debugger_sp = m_interpreter->GetCommandInterpreter().GetDebugger().shared_from_this(); + + if (!m_debugger_sp) + { + m_error = true; + return; + } + + m_reader_sp = InputReaderSP(new InputReader(*m_debugger_sp.get())); + + if (!m_reader_sp) + { + m_error = true; + return; + } + + Error error (m_reader_sp->Initialize (ScriptInterpreterPython::PythonInputReaderManager::InputReaderCallback, + m_interpreter, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + if (error.Fail()) + m_error = true; + else + { + m_debugger_sp->PushInputReader (m_reader_sp); + m_interpreter->m_embedded_thread_input_reader_sp = m_reader_sp; + } +} + +ScriptInterpreterPython::PythonInputReaderManager::~PythonInputReaderManager() +{ + // Nothing to do if either m_interpreter or m_reader_sp is invalid. + if (!m_interpreter || !m_reader_sp) + return; + + m_reader_sp->SetIsDone (true); + if (m_debugger_sp) + m_debugger_sp->PopInputReader(m_reader_sp); + + // Only mess with m_interpreter's counterpart if, indeed, they are the same object. + if (m_reader_sp.get() == m_interpreter->m_embedded_thread_input_reader_sp.get()) + { + m_interpreter->m_embedded_thread_pty.CloseSlaveFileDescriptor(); + m_interpreter->m_embedded_thread_input_reader_sp.reset(); + } +} + +size_t +ScriptInterpreterPython::PythonInputReaderManager::InputReaderCallback (void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len) +{ + lldb::thread_t embedded_interpreter_thread; + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (baton == NULL) + return 0; + + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + if (script_interpreter->m_script_lang != eScriptLanguagePython) + return 0; + + switch (notification) + { + case eInputReaderActivate: + { + // Save terminal settings if we can + int input_fd = reader.GetDebugger().GetInputFile().GetDescriptor(); + if (input_fd == File::kInvalidDescriptor) + input_fd = STDIN_FILENO; + + script_interpreter->SaveTerminalState(input_fd); + + char error_str[1024]; + if (script_interpreter->m_embedded_thread_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str, + sizeof(error_str))) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, succeeded in opening master pty (fd = %d).", + script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor()); + { + StreamString run_string; + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_thread_pty.GetSlaveName (error_str, sizeof (error_str)); + if (pty_slave_name != NULL && PyThreadState_GetDict() != NULL) + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + run_string.Printf ("run_one_line (%s, 'save_stderr = sys.stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = sys.stdout')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'save_stdin = sys.stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, \"sys.stdin = open ('%s', 'r')\")", script_interpreter->m_dictionary_name.c_str(), + pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + } + } + embedded_interpreter_thread = Host::ThreadCreate ("", + ScriptInterpreterPython::PythonInputReaderManager::RunPythonInputReader, + script_interpreter, NULL); + if (IS_VALID_LLDB_HOST_THREAD(embedded_interpreter_thread)) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, succeeded in creating thread (thread_t = %p)", (void *)embedded_interpreter_thread); + Error detach_error; + Host::ThreadDetach (embedded_interpreter_thread, &detach_error); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, failed in creating thread"); + reader.SetIsDone (true); + } + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Activate, failed to open master pty "); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderDeactivate: + // When another input reader is pushed, don't leave the session... + //script_interpreter->LeaveSession (); + break; + + case eInputReaderReactivate: +// { +// ScriptInterpreterPython::Locker locker(script_interpreter, +// ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession, +// ScriptInterpreterPython::Locker::FreeAcquiredLock); +// } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + { + PyThreadState* state = _PyThreadState_Current; + if (!state) + state = script_interpreter->m_command_thread_state; + if (state) + { + long tid = state->thread_id; + _PyThreadState_Current = state; + int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); + if (log) + log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, tid = %ld, num_threads = %d, state = %p", + tid,num_threads,state); + } + else if (log) + log->Printf("ScriptInterpreterPython::NonInteractiveInputReaderCallback, eInputReaderInterrupt, state = NULL"); + } + break; + + case eInputReaderEndOfFile: + reader.SetIsDone(true); + break; + + case eInputReaderGotToken: + if (script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor() != -1) + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, GotToken, bytes='%s', byte_len = %lu", bytes, + bytes_len); + if (bytes && bytes_len) + ::write (script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor(), bytes, bytes_len); + ::write (script_interpreter->m_embedded_thread_pty.GetMasterFileDescriptor(), "\n", 1); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, GotToken, bytes='%s', byte_len = %lu, Master File Descriptor is bad.", + bytes, + bytes_len); + reader.SetIsDone (true); + } + break; + + case eInputReaderDone: + { + StreamString run_string; + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_thread_pty.GetSlaveName (error_str, sizeof (error_str)); + if (pty_slave_name != NULL && PyThreadState_GetDict() != NULL) + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + run_string.Printf ("run_one_line (%s, 'sys.stdin = save_stdin; sys.stderr = save_stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + } + // Restore terminal settings if they were validly saved + if (log) + log->Printf ("ScriptInterpreterPython::NonInteractiveInputReaderCallback, Done, closing down input reader."); + + script_interpreter->RestoreTerminalState (); + + script_interpreter->m_embedded_thread_pty.CloseMasterFileDescriptor(); + } + break; + } + + return bytes_len; +} + +ScriptInterpreterPython::ScriptInterpreterPython (CommandInterpreter &interpreter) : + ScriptInterpreter (interpreter, eScriptLanguagePython), + m_embedded_thread_pty (), + m_embedded_python_pty (), + m_embedded_thread_input_reader_sp (), + m_embedded_python_input_reader_sp (), + m_dbg_stdout (interpreter.GetDebugger().GetOutputFile().GetStream()), + m_new_sysout (NULL), + m_old_sysout (NULL), + m_old_syserr (NULL), + m_run_one_line (NULL), + m_dictionary_name (interpreter.GetDebugger().GetInstanceName().AsCString()), + m_terminal_state (), + m_session_is_active (false), + m_valid_session (true), + m_command_thread_state (NULL) +{ + + static int g_initialized = false; + + if (!g_initialized) + { + g_initialized = true; + ScriptInterpreterPython::InitializePrivate (); + } + + m_dictionary_name.append("_dict"); + StreamString run_string; + run_string.Printf ("%s = dict()", m_dictionary_name.c_str()); + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + PyRun_SimpleString (run_string.GetData()); + + run_string.Clear(); + + // Importing 'lldb' module calls SBDebugger::Initialize, which calls Debugger::Initialize, which increments a + // global debugger ref-count; therefore we need to check the ref-count before and after importing lldb, and if the + // ref-count increased we need to call Debugger::Terminate here to decrement the ref-count so that when the final + // call to Debugger::Terminate is made, the ref-count has the correct value. + // + // Bonus question: Why doesn't the ref-count always increase? Because sometimes lldb has already been imported, in + // which case the code inside it, including the call to SBDebugger::Initialize(), does not get executed. + + int old_count = Debugger::TestDebuggerRefCount(); + + run_string.Printf ("run_one_line (%s, 'import copy, os, re, sys, uuid, lldb')", 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()); + + int new_count = Debugger::TestDebuggerRefCount(); + + if (new_count > old_count) + Debugger::Terminate(); + + run_string.Clear(); + run_string.Printf ("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64 "; pydoc.pager = pydoc.plainpager')", m_dictionary_name.c_str(), + interpreter.GetDebugger().GetID()); + PyRun_SimpleString (run_string.GetData()); + + if (m_dbg_stdout != NULL) + { + m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush); + } + + // get the output file handle from the debugger (if any) + File& out_file = interpreter.GetDebugger().GetOutputFile(); + if (out_file.IsValid()) + ResetOutputFileHandle(out_file.GetStream()); +} + +ScriptInterpreterPython::~ScriptInterpreterPython () +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + if (m_embedded_thread_input_reader_sp.get() != NULL) + { + m_embedded_thread_input_reader_sp->SetIsDone (true); + m_embedded_thread_pty.CloseSlaveFileDescriptor(); + const InputReaderSP reader_sp = m_embedded_thread_input_reader_sp; + debugger.PopInputReader (reader_sp); + m_embedded_thread_input_reader_sp.reset(); + } + + if (m_embedded_python_input_reader_sp.get() != NULL) + { + m_embedded_python_input_reader_sp->SetIsDone (true); + m_embedded_python_pty.CloseSlaveFileDescriptor(); + const InputReaderSP reader_sp = m_embedded_python_input_reader_sp; + debugger.PopInputReader (reader_sp); + m_embedded_python_input_reader_sp.reset(); + } + + if (m_new_sysout) + { + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeLock); + Py_XDECREF ((PyObject*)m_new_sysout); + } +} + +void +ScriptInterpreterPython::ResetOutputFileHandle (FILE *fh) +{ + if (fh == NULL) + return; + + m_dbg_stdout = fh; + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + + m_new_sysout = PyFile_FromFile (m_dbg_stdout, (char *) "", (char *) "w", _check_and_flush); +} + +void +ScriptInterpreterPython::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 +ScriptInterpreterPython::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 +ScriptInterpreterPython::LeaveSession () +{ + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + if (log) + log->PutCString("ScriptInterpreterPython::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()) + { + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if (m_new_sysout && sysmod && sysdict) + { + if (m_old_sysout) + PyDict_SetItemString (sysdict, "stdout", (PyObject*)m_old_sysout); + if (m_old_syserr) + PyDict_SetItemString (sysdict, "stderr", (PyObject*)m_old_syserr); + } + } + + m_session_is_active = false; +} + +bool +ScriptInterpreterPython::EnterSession (bool init_lldb_globals) +{ + // 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("ScriptInterpreterPython::EnterSession(init_lldb_globals=%i) session is already active, returning without doing anything", init_lldb_globals); + return false; + } + + if (log) + log->Printf("ScriptInterpreterPython::EnterSession(init_lldb_globals=%i)", init_lldb_globals); + + + m_session_is_active = true; + + StreamString run_string; + + if (init_lldb_globals) + { + run_string.Printf ( "run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64, m_dictionary_name.c_str(), GetCommandInterpreter().GetDebugger().GetID()); + run_string.Printf ( "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")", GetCommandInterpreter().GetDebugger().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(), GetCommandInterpreter().GetDebugger().GetID()); + run_string.Printf ( "; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")", GetCommandInterpreter().GetDebugger().GetID()); + run_string.PutCString ("\")"); + } + + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + + PyObject *sysmod = PyImport_AddModule ("sys"); + PyObject *sysdict = PyModule_GetDict (sysmod); + + if (m_new_sysout && sysmod && sysdict) + { + m_old_sysout = PyDict_GetItemString(sysdict, "stdout"); + m_old_syserr = PyDict_GetItemString(sysdict, "stderr"); + if (m_new_sysout) + { + PyDict_SetItemString (sysdict, "stdout", (PyObject*)m_new_sysout); + PyDict_SetItemString (sysdict, "stderr", (PyObject*)m_new_sysout); + } + } + + if (PyErr_Occurred()) + PyErr_Clear (); + + return true; +} + +static PyObject* +FindSessionDictionary (const char* dict_name) +{ + static std::map g_dict_map; + + ConstString dict(dict_name); + + std::map::iterator iter = g_dict_map.find(dict); + + if (iter != g_dict_map.end()) + return iter->second; + + PyObject *main_mod = PyImport_AddModule ("__main__"); + if (main_mod != NULL) + { + PyObject *main_dict = PyModule_GetDict (main_mod); + if ((main_dict != NULL) + && PyDict_Check (main_dict)) + { + // Go through the main dictionary looking for the correct python script interpreter dictionary + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next (main_dict, &pos, &key, &value)) + { + // We have stolen references to the key and value objects in the dictionary; we need to increment + // them now so that Python's garbage collector doesn't collect them out from under us. + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), dict_name) == 0) + { + g_dict_map[dict] = value; + return value; + } + } + } + } + return NULL; +} + +static std::string +GenerateUniqueName (const char* base_name_wanted, + uint32_t& functions_counter, + void* name_token = NULL) +{ + 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 +ScriptInterpreterPython::ExecuteOneLine (const char *command, CommandReturnObject *result, const ExecuteScriptOptions &options) +{ + if (!m_valid_session) + return false; + + // 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. + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + bool success = false; + + if (command) + { + // Find the correct script interpreter dictionary in the main module. + PyObject *script_interpreter_dict = FindSessionDictionary(m_dictionary_name.c_str()); + if (script_interpreter_dict != NULL) + { + PyObject *pfunc = (PyObject*)m_run_one_line; + PyObject *pmod = PyImport_AddModule ("lldb.embedded_interpreter"); + if (pmod != NULL) + { + PyObject *pmod_dict = PyModule_GetDict (pmod); + if ((pmod_dict != NULL) + && PyDict_Check (pmod_dict)) + { + if (!pfunc) + { + PyObject *key, *value; + Py_ssize_t pos = 0; + + while (PyDict_Next (pmod_dict, &pos, &key, &value)) + { + Py_INCREF (key); + Py_INCREF (value); + if (strcmp (PyString_AsString (key), "run_one_line") == 0) + { + pfunc = value; + break; + } + } + m_run_one_line = pfunc; + } + + if (pfunc && PyCallable_Check (pfunc)) + { + PyObject *pargs = Py_BuildValue("(Os)",script_interpreter_dict,command); + if (pargs != NULL) + { + PyObject *pvalue = NULL; + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); + pvalue = PyObject_CallObject (pfunc, pargs); + } + Py_XDECREF (pargs); + if (pvalue != NULL) + { + Py_XDECREF (pvalue); + success = true; + } + else if (options.GetMaskoutErrors() && PyErr_Occurred ()) + { + PyErr_Print(); + PyErr_Clear(); + } + } + } + } + } + Py_INCREF (script_interpreter_dict); + } + + if (success) + return true; + + // The one-liner failed. Append the error message. + if (result) + result->AppendErrorWithFormat ("python failed attempting to evaluate '%s'\n", command); + return false; + } + + if (result) + result->AppendError ("empty command passed to python\n"); + return false; +} + +size_t +ScriptInterpreterPython::InputReaderCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + lldb::thread_t embedded_interpreter_thread; + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (baton == NULL) + return 0; + + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + if (script_interpreter->m_script_lang != eScriptLanguagePython) + return 0; + + switch (notification) + { + case eInputReaderActivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (!batch_mode) + { + out_stream->Printf ("Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.\n"); + out_stream->Flush(); + } + + // Save terminal settings if we can + int input_fd = reader.GetDebugger().GetInputFile().GetDescriptor(); + if (input_fd == File::kInvalidDescriptor) + input_fd = STDIN_FILENO; + + script_interpreter->SaveTerminalState(input_fd); + + { + ScriptInterpreterPython::Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + } + + char error_str[1024]; + if (script_interpreter->m_embedded_python_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, error_str, + sizeof(error_str))) + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, succeeded in opening master pty (fd = %d).", + script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor()); + embedded_interpreter_thread = Host::ThreadCreate ("", + ScriptInterpreterPython::RunEmbeddedPythonInterpreter, + script_interpreter, NULL); + if (IS_VALID_LLDB_HOST_THREAD(embedded_interpreter_thread)) + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, succeeded in creating thread (thread_t = %p)", (void *)embedded_interpreter_thread); + Error detach_error; + Host::ThreadDetach (embedded_interpreter_thread, &detach_error); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, failed in creating thread"); + reader.SetIsDone (true); + } + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Activate, failed to open master pty "); + reader.SetIsDone (true); + } + } + break; + + case eInputReaderDeactivate: + // When another input reader is pushed, don't leave the session... + //script_interpreter->LeaveSession (); + break; + + case eInputReaderReactivate: + { + ScriptInterpreterPython::Locker locker (script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderInterrupt: + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "raise KeyboardInterrupt\n", 24); + break; + + case eInputReaderEndOfFile: + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "quit()\n", 7); + break; + + case eInputReaderGotToken: + if (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor() != -1) + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, GotToken, bytes='%s', byte_len = %lu", bytes, + bytes_len); + if (bytes && bytes_len) + { + if ((int) bytes[0] == 4) + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "quit()", 6); + else + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), bytes, bytes_len); + } + ::write (script_interpreter->m_embedded_python_pty.GetMasterFileDescriptor(), "\n", 1); + } + else + { + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, GotToken, bytes='%s', byte_len = %lu, Master File Descriptor is bad.", + bytes, + bytes_len); + reader.SetIsDone (true); + } + + break; + + case eInputReaderDone: + { + Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock, + ScriptInterpreterPython::Locker::FreeAcquiredLock); + script_interpreter->LeaveSession (); + } + + // Restore terminal settings if they were validly saved + if (log) + log->Printf ("ScriptInterpreterPython::InputReaderCallback, Done, closing down input reader."); + + script_interpreter->RestoreTerminalState (); + + script_interpreter->m_embedded_python_pty.CloseMasterFileDescriptor(); + break; + } + + return bytes_len; +} + + +void +ScriptInterpreterPython::ExecuteInterpreterLoop () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + // 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().IsValid()) + return; + + InputReaderSP reader_sp (new InputReader(debugger)); + if (reader_sp) + { + Error error (reader_sp->Initialize (ScriptInterpreterPython::InputReaderCallback, + this, // baton + eInputReaderGranularityLine, // token size, to pass to callback function + NULL, // end token + NULL, // prompt + true)); // echo input + + if (error.Success()) + { + debugger.PushInputReader (reader_sp); + m_embedded_python_input_reader_sp = reader_sp; + } + } +} + +bool +ScriptInterpreterPython::ExecuteOneLineWithReturn (const char *in_string, + ScriptInterpreter::ScriptReturnType return_type, + void *ret_value, + const ExecuteScriptOptions &options) +{ + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = NULL; + PyObject *py_error = NULL; + bool ret_success = false; + bool should_decrement_locals = false; + int success; + + locals = FindSessionDictionary(m_dictionary_name.c_str()); + + if (locals == NULL) + { + locals = PyObject_GetAttrString (globals, m_dictionary_name.c_str()); + should_decrement_locals = true; + } + + if (locals == NULL) + { + locals = globals; + should_decrement_locals = false; + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + PyErr_Clear(); + + if (in_string != NULL) + { + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); + py_return = PyRun_String (in_string, Py_eval_input, globals, locals); + if (py_return == NULL) + { + py_error = PyErr_Occurred (); + if (py_error != NULL) + PyErr_Clear (); + + py_return = PyRun_String (in_string, Py_single_input, globals, locals); + } + } + + if (locals != NULL + && should_decrement_locals) + Py_XDECREF (locals); + + if (py_return != NULL) + { + switch (return_type) + { + case eScriptReturnTypeCharPtr: // "char *" + { + const char format[3] = "s#"; + success = PyArg_Parse (py_return, 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, format, (char **) ret_value); + break; + } + case eScriptReturnTypeBool: + { + const char format[2] = "b"; + success = PyArg_Parse (py_return, format, (bool *) ret_value); + break; + } + case eScriptReturnTypeShortInt: + { + const char format[2] = "h"; + success = PyArg_Parse (py_return, format, (short *) ret_value); + break; + } + case eScriptReturnTypeShortIntUnsigned: + { + const char format[2] = "H"; + success = PyArg_Parse (py_return, format, (unsigned short *) ret_value); + break; + } + case eScriptReturnTypeInt: + { + const char format[2] = "i"; + success = PyArg_Parse (py_return, format, (int *) ret_value); + break; + } + case eScriptReturnTypeIntUnsigned: + { + const char format[2] = "I"; + success = PyArg_Parse (py_return, format, (unsigned int *) ret_value); + break; + } + case eScriptReturnTypeLongInt: + { + const char format[2] = "l"; + success = PyArg_Parse (py_return, format, (long *) ret_value); + break; + } + case eScriptReturnTypeLongIntUnsigned: + { + const char format[2] = "k"; + success = PyArg_Parse (py_return, format, (unsigned long *) ret_value); + break; + } + case eScriptReturnTypeLongLong: + { + const char format[2] = "L"; + success = PyArg_Parse (py_return, format, (long long *) ret_value); + break; + } + case eScriptReturnTypeLongLongUnsigned: + { + const char format[2] = "K"; + success = PyArg_Parse (py_return, format, (unsigned long long *) ret_value); + break; + } + case eScriptReturnTypeFloat: + { + const char format[2] = "f"; + success = PyArg_Parse (py_return, format, (float *) ret_value); + break; + } + case eScriptReturnTypeDouble: + { + const char format[2] = "d"; + success = PyArg_Parse (py_return, format, (double *) ret_value); + break; + } + case eScriptReturnTypeChar: + { + const char format[2] = "c"; + success = PyArg_Parse (py_return, format, (char *) ret_value); + break; + } + } + Py_XDECREF (py_return); + if (success) + ret_success = true; + else + ret_success = false; + } + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + { + ret_success = false; + if (options.GetMaskoutErrors()) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + } + } + + return ret_success; +} + +bool +ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string, const ExecuteScriptOptions &options) +{ + + + Locker locker(this, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | (options.GetSetLLDBGlobals() ? ScriptInterpreterPython::Locker::InitGlobals : 0), + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + bool success = false; + PyObject *py_return = NULL; + PyObject *mainmod = PyImport_AddModule ("__main__"); + PyObject *globals = PyModule_GetDict (mainmod); + PyObject *locals = NULL; + PyObject *py_error = NULL; + bool should_decrement_locals = false; + + locals = FindSessionDictionary(m_dictionary_name.c_str()); + + if (locals == NULL) + { + locals = PyObject_GetAttrString (globals, m_dictionary_name.c_str()); + should_decrement_locals = true; + } + + if (locals == NULL) + { + locals = globals; + should_decrement_locals = false; + } + + py_error = PyErr_Occurred(); + if (py_error != NULL) + PyErr_Clear(); + + if (in_string != NULL) + { + struct _node *compiled_node = PyParser_SimpleParseString (in_string, Py_file_input); + if (compiled_node) + { + PyCodeObject *compiled_code = PyNode_Compile (compiled_node, "temp.py"); + if (compiled_code) + { + { // scope for PythonInputReaderManager + PythonInputReaderManager py_input(options.GetEnableIO() ? this : NULL); + py_return = PyEval_EvalCode (compiled_code, globals, locals); + } + if (py_return != NULL) + { + success = true; + Py_XDECREF (py_return); + } + if (locals && should_decrement_locals) + Py_XDECREF (locals); + } + } + } + + py_error = PyErr_Occurred (); + if (py_error != NULL) + { + success = false; + if (options.GetMaskoutErrors()) + { + if (PyErr_GivenExceptionMatches (py_error, PyExc_SyntaxError)) + PyErr_Print (); + PyErr_Clear(); + } + } + + return success; +} + +static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end."; + +static const char *g_bkpt_command_reader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function(frame,bp_loc,internal_dict):\n" + " \"\"\"frame: the SBFrame for the location at which you stopped\n" + " bp_loc: an SBBreakpointLocation for the breakpoint location information\n" + " internal_dict: an LLDB support object not to be used\"\"\""; + +size_t +ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + static StringList commands_in_progress; + + switch (notification) + { + case eInputReaderActivate: + { + + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + commands_in_progress.Clear(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_bkpt_command_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + std::string temp_string (bytes, bytes_len); + commands_in_progress.AppendString (temp_string.c_str()); + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderEndOfFile: + case eInputReaderInterrupt: + // Control-c (SIGINT) & control-d both mean finish & exit. + reader.SetIsDone(true); + + // Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command. + if (notification == eInputReaderInterrupt) + commands_in_progress.Clear(); + + // Fall through here... + + case eInputReaderDone: + { + bool batch_mode = notification == eInputReaderDone ? + reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode() : + true; + BreakpointOptions *bp_options = (BreakpointOptions *)baton; + std::unique_ptr data_ap(new BreakpointOptions::CommandData()); + data_ap->user_source.AppendList (commands_in_progress); + if (data_ap.get()) + { + ScriptInterpreter *interpreter = reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter) + { + if (interpreter->GenerateBreakpointCommandCallbackData (data_ap->user_source, + data_ap->script_source)) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + } + else if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + else + { + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: Unable to find script intepreter; no command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + } + } + break; + + } + + return bytes_len; +} + +size_t +ScriptInterpreterPython::GenerateWatchpointOptionsCommandCallback +( + void *baton, + InputReader &reader, + InputReaderAction notification, + const char *bytes, + size_t bytes_len +) +{ + static StringList commands_in_progress; + + switch (notification) + { + case eInputReaderActivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + + commands_in_progress.Clear(); + if (!batch_mode) + { + out_stream->Printf ("%s\n", g_reader_instructions); + if (reader.GetPrompt()) + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderDeactivate: + break; + + case eInputReaderReactivate: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + if (reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderAsynchronousOutputWritten: + break; + + case eInputReaderGotToken: + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); + std::string temp_string (bytes, bytes_len); + commands_in_progress.AppendString (temp_string.c_str()); + if (!reader.IsDone() && reader.GetPrompt() && !batch_mode) + { + out_stream->Printf ("%s", reader.GetPrompt()); + out_stream->Flush (); + } + } + break; + + case eInputReaderEndOfFile: + case eInputReaderInterrupt: + // Control-c (SIGINT) & control-d both mean finish & exit. + reader.SetIsDone(true); + + // Control-c (SIGINT) ALSO means cancel; do NOT create a breakpoint command. + if (notification == eInputReaderInterrupt) + commands_in_progress.Clear(); + + // Fall through here... + + case eInputReaderDone: + { + bool batch_mode = notification == eInputReaderDone ? + reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode() : + true; + WatchpointOptions *wp_options = (WatchpointOptions *)baton; + std::unique_ptr data_ap(new WatchpointOptions::CommandData()); + data_ap->user_source.AppendList (commands_in_progress); + if (data_ap.get()) + { + ScriptInterpreter *interpreter = reader.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); + if (interpreter) + { + if (interpreter->GenerateWatchpointCommandCallbackData (data_ap->user_source, + data_ap->script_source)) + { + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp); + } + else if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: No command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + else + { + if (!batch_mode) + { + StreamSP out_stream = reader.GetDebugger().GetAsyncOutputStream(); + out_stream->Printf ("Warning: Unable to find script intepreter; no command attached to breakpoint.\n"); + out_stream->Flush(); + } + } + } + } + break; + + } + + return bytes_len; +} + +void +ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOptions *bp_options, + CommandReturnObject &result) +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + InputReaderSP reader_sp (new InputReader (debugger)); + + if (reader_sp) + { + Error err = reader_sp->Initialize ( + ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback, + bp_options, // baton + eInputReaderGranularityLine, // token size, for feeding data to callback function + "DONE", // end token + " ", // prompt + true); // echo input + + if (err.Success()) + debugger.PushInputReader (reader_sp); + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +void +ScriptInterpreterPython::CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options, + CommandReturnObject &result) +{ + Debugger &debugger = GetCommandInterpreter().GetDebugger(); + + InputReaderSP reader_sp (new InputReader (debugger)); + + if (reader_sp) + { + Error err = reader_sp->Initialize ( + ScriptInterpreterPython::GenerateWatchpointOptionsCommandCallback, + wp_options, // baton + eInputReaderGranularityLine, // token size, for feeding data to callback function + "DONE", // end token + "> ", // prompt + true); // echo input + + if (err.Success()) + debugger.PushInputReader (reader_sp); + else + { + result.AppendError (err.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + else + { + result.AppendError("out of memory"); + result.SetStatus (eReturnStatusFailed); + } +} + +// Set a Python one-liner as the callback for the breakpoint. +void +ScriptInterpreterPython::SetBreakpointCommandCallback (BreakpointOptions *bp_options, + const char *oneliner) +{ + std::unique_ptr data_ap(new BreakpointOptions::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 breakpoint command list) + // while the latter is used for Python to interpret during the actual callback. + + data_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + + if (GenerateBreakpointCommandCallbackData (data_ap->user_source, data_ap->script_source)) + { + BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release())); + bp_options->SetCallback (ScriptInterpreterPython::BreakpointCallbackFunction, baton_sp); + } + + return; +} + +// Set a Python one-liner as the callback for the watchpoint. +void +ScriptInterpreterPython::SetWatchpointCommandCallback (WatchpointOptions *wp_options, + const char *oneliner) +{ + std::unique_ptr data_ap(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_ap->user_source.AppendString (oneliner); + data_ap->script_source.assign (oneliner); + + if (GenerateWatchpointCommandCallbackData (data_ap->user_source, data_ap->script_source)) + { + BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release())); + wp_options->SetCallback (ScriptInterpreterPython::WatchpointCallbackFunction, baton_sp); + } + + return; +} + +bool +ScriptInterpreterPython::ExportFunctionDefinitionToInterpreter (StringList &function_def) +{ + // Convert StringList to one long, newline delimited, const char *. + std::string function_def_string(function_def.CopyList()); + + return ExecuteMultipleLines (function_def_string.c_str(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false)); +} + +bool +ScriptInterpreterPython::GenerateFunction(const char *signature, const StringList &input) +{ + int num_lines = input.GetSize (); + if (num_lines == 0) + return false; + + if (!signature || *signature == 0) + return false; + + 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. + + if (!ExportFunctionDefinitionToInterpreter (auto_generated_function)) + return false; + + return true; + +} + +bool +ScriptInterpreterPython::GenerateTypeScriptFunction (StringList &user_input, std::string& output, 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)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + +bool +ScriptInterpreterPython::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)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + + +bool +ScriptInterpreterPython::GenerateTypeSynthClass (StringList &user_input, std::string &output, 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.GetData()); + + // 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.GetData()); + } + + + // 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)) + return false; + + // Store the name of the auto-generated class + + output.assign(auto_generated_class_name); + return true; +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_CreatePluginObject (const char *class_name, lldb::ProcessSP process_sp) +{ + if (class_name == NULL || class_name[0] == '\0') + return lldb::ScriptInterpreterObjectSP(); + + if (!process_sp) + return lldb::ScriptInterpreterObjectSP(); + + void* ret_val; + + { + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + ret_val = g_swig_create_os_plugin (class_name, + m_dictionary_name.c_str(), + process_sp); + } + + return MakeScriptObject(ret_val); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_RegisterInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "get_register_info"; + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, NULL); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_ThreadsInfo (lldb::ScriptInterpreterObjectSP os_plugin_object_sp) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "get_thread_info"; + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, NULL); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +// 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 +const char *GetPythonValueFormatString(T t) +{ + assert(!"Unhandled type passed to GetPythonValueFormatString(T), make a specialization of GetPythonValueFormatString() to support this type."); + return NULL; +} +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"; } + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_RegisterContextData (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid) +{ + Locker py_lock(this,Locker::AcquireLock,Locker::FreeLock); + + static char callee_name[] = "get_register_data"; + static char *param_format = const_cast(GetPythonValueFormatString(tid)); + + if (!os_plugin_object_sp) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, param_format, tid); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::OSPlugin_CreateThread (lldb::ScriptInterpreterObjectSP os_plugin_object_sp, + lldb::tid_t tid, + lldb::addr_t context) +{ + Locker py_lock(this,Locker::AcquireLock,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 lldb::ScriptInterpreterObjectSP(); + + PyObject* implementor = (PyObject*)os_plugin_object_sp->GetObject(); + + if (implementor == NULL || implementor == Py_None) + return lldb::ScriptInterpreterObjectSP(); + + PyObject* pmeth = PyObject_GetAttrString(implementor, callee_name); + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + if (pmeth == NULL || pmeth == Py_None) + { + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyCallable_Check(pmeth) == 0) + { + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + return lldb::ScriptInterpreterObjectSP(); + } + + if (PyErr_Occurred()) + { + PyErr_Clear(); + } + + Py_XDECREF(pmeth); + + // right now we know this function exists and is callable.. + PyObject* py_return = PyObject_CallMethod(implementor, callee_name, ¶m_format[0], tid, context); + + // if it fails, print the error but otherwise go on + if (PyErr_Occurred()) + { + PyErr_Print(); + PyErr_Clear(); + } + + return MakeScriptObject(py_return); +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::CreateSyntheticScriptedProvider (const char *class_name, + lldb::ValueObjectSP valobj) +{ + if (class_name == NULL || class_name[0] == '\0') + return lldb::ScriptInterpreterObjectSP(); + + if (!valobj.get()) + return lldb::ScriptInterpreterObjectSP(); + + ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); + Target *target = exe_ctx.GetTargetPtr(); + + if (!target) + return lldb::ScriptInterpreterObjectSP(); + + Debugger &debugger = target->GetDebugger(); + ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return lldb::ScriptInterpreterObjectSP(); + + void* ret_val; + + { + Locker py_lock(this); + ret_val = g_swig_synthetic_script (class_name, + python_interpreter->m_dictionary_name.c_str(), + valobj); + } + + return MakeScriptObject(ret_val); +} + +bool +ScriptInterpreterPython::GenerateTypeScriptFunction (const char* oneliner, std::string& output, void* name_token) +{ + StringList input; + input.SplitIntoLines(oneliner, strlen(oneliner)); + return GenerateTypeScriptFunction(input, output, name_token); +} + +bool +ScriptInterpreterPython::GenerateTypeSynthClass (const char* oneliner, std::string& output, void* name_token) +{ + StringList input; + input.SplitIntoLines(oneliner, strlen(oneliner)); + return GenerateTypeSynthClass(input, output, name_token); +} + + +bool +ScriptInterpreterPython::GenerateBreakpointCommandCallbackData (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_bp_callback_func_",num_created_functions)); + sstr.Printf ("def %s (frame, bp_loc, internal_dict):", auto_generated_function_name.c_str()); + + if (!GenerateFunction(sstr.GetData(), user_input)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + +bool +ScriptInterpreterPython::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)) + return false; + + // Store the name of the auto-generated function to be called. + output.assign(auto_generated_function_name); + return true; +} + +bool +ScriptInterpreterPython::GetScriptedSummary (const char *python_function_name, + lldb::ValueObjectSP valobj, + lldb::ScriptInterpreterObjectSP& callee_wrapper_sp, + std::string& retval) +{ + + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + if (!valobj.get()) + { + retval.assign(""); + return false; + } + + void* old_callee = (callee_wrapper_sp ? callee_wrapper_sp->GetObject() : NULL); + void* new_callee = old_callee; + + bool ret_val; + if (python_function_name + && *python_function_name) + { + { + Locker py_lock(this); + { + Timer scoped_timer ("g_swig_typescript_callback","g_swig_typescript_callback"); + ret_val = g_swig_typescript_callback (python_function_name, + FindSessionDictionary(m_dictionary_name.c_str()), + valobj, + &new_callee, + retval); + } + } + } + else + { + retval.assign(""); + return false; + } + + if (new_callee && old_callee != new_callee) + callee_wrapper_sp = MakeScriptObject(new_callee); + + return ret_val; + +} + +bool +ScriptInterpreterPython::BreakpointCallbackFunction +( + void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id +) +{ + BreakpointOptions::CommandData *bp_option_data = (BreakpointOptions::CommandData *) 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.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return true; + + if (python_function_name != NULL + && python_function_name[0] != '\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); + ret_val = g_swig_breakpoint_callback (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 +ScriptInterpreterPython::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.GetCommandInterpreter().GetScriptInterpreter(); + ScriptInterpreterPython *python_interpreter = (ScriptInterpreterPython *) script_interpreter; + + if (!script_interpreter) + return true; + + if (python_function_name != NULL + && python_function_name[0] != '\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); + ret_val = g_swig_watchpoint_callback (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; +} + +lldb::thread_result_t +ScriptInterpreterPython::RunEmbeddedPythonInterpreter (lldb::thread_arg_t baton) +{ + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT)); + + if (log) + log->Printf ("%p ScriptInterpreterPython::RunEmbeddedPythonInterpreter () thread starting...", baton); + + char error_str[1024]; + const char *pty_slave_name = script_interpreter->m_embedded_python_pty.GetSlaveName (error_str, sizeof (error_str)); + + if (pty_slave_name != NULL) + { + StreamString run_string; + + // Ensure we have the GIL before running any Python code. + // Since we're only running a few one-liners and then dropping to the interpreter (which will release the GIL when needed), + // we can just release the GIL after finishing our work. + // If finer-grained locking is desirable, we can lock and unlock the GIL only when calling a python function. + Locker locker(script_interpreter, + ScriptInterpreterPython::Locker::AcquireLock | ScriptInterpreterPython::Locker::InitSession | ScriptInterpreterPython::Locker::InitGlobals, + ScriptInterpreterPython::Locker::FreeAcquiredLock | ScriptInterpreterPython::Locker::TearDownSession); + + run_string.Printf ("run_one_line (%s, 'save_stderr = sys.stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = sys.stdout')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'save_stdin = sys.stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, \"sys.stdin = open ('%s', 'r')\")", script_interpreter->m_dictionary_name.c_str(), + pty_slave_name); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + // 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). + + run_string.Printf ("run_python_interpreter (%s)", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear (); + + run_string.Printf ("run_one_line (%s, 'sys.stdin = save_stdin')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + + run_string.Printf ("run_one_line (%s, 'sys.stderr = save_stderr')", script_interpreter->m_dictionary_name.c_str()); + PyRun_SimpleString (run_string.GetData()); + run_string.Clear(); + } + + if (script_interpreter->m_embedded_python_input_reader_sp) + script_interpreter->m_embedded_python_input_reader_sp->SetIsDone (true); + + script_interpreter->m_embedded_python_pty.CloseSlaveFileDescriptor(); + + log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT); + if (log) + log->Printf ("%p ScriptInterpreterPython::RunEmbeddedPythonInterpreter () thread exiting...", baton); + + + // Clean up the input reader and make the debugger pop it off the stack. + Debugger &debugger = script_interpreter->GetCommandInterpreter().GetDebugger(); + const InputReaderSP reader_sp = script_interpreter->m_embedded_python_input_reader_sp; + if (reader_sp) + { + debugger.PopInputReader (reader_sp); + script_interpreter->m_embedded_python_input_reader_sp.reset(); + } + + return NULL; +} + +lldb::thread_result_t +ScriptInterpreterPython::PythonInputReaderManager::RunPythonInputReader (lldb::thread_arg_t baton) +{ + ScriptInterpreterPython *script_interpreter = (ScriptInterpreterPython *) baton; + + const InputReaderSP reader_sp = script_interpreter->m_embedded_thread_input_reader_sp; + + if (reader_sp) + reader_sp->WaitOnReaderIsDone(); + + return NULL; +} + +size_t +ScriptInterpreterPython::CalculateNumChildren (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + if (!implementor_sp) + return 0; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return 0; + + if (!g_swig_calc_children) + return 0; + + uint32_t ret_val = 0; + + { + Locker py_lock(this); + ret_val = g_swig_calc_children (implementor); + } + + return ret_val; +} + +lldb::ValueObjectSP +ScriptInterpreterPython::GetChildAtIndex (const lldb::ScriptInterpreterObjectSP& implementor_sp, uint32_t idx) +{ + if (!implementor_sp) + return lldb::ValueObjectSP(); + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return lldb::ValueObjectSP(); + + if (!g_swig_get_child_index || !g_swig_cast_to_sbvalue) + return lldb::ValueObjectSP(); + + void* child_ptr = NULL; + lldb::SBValue* value_sb = NULL; + lldb::ValueObjectSP ret_val; + + { + Locker py_lock(this); + child_ptr = g_swig_get_child_index (implementor,idx); + if (child_ptr != NULL && child_ptr != Py_None) + { + value_sb = (lldb::SBValue*)g_swig_cast_to_sbvalue(child_ptr); + if (value_sb == NULL) + Py_XDECREF(child_ptr); + else + ret_val = value_sb->GetSP(); + } + else + { + Py_XDECREF(child_ptr); + } + } + + return ret_val; +} + +int +ScriptInterpreterPython::GetIndexOfChildWithName (const lldb::ScriptInterpreterObjectSP& implementor_sp, const char* child_name) +{ + if (!implementor_sp) + return UINT32_MAX; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return UINT32_MAX; + + if (!g_swig_get_index_child) + return UINT32_MAX; + + int ret_val = UINT32_MAX; + + { + Locker py_lock(this); + ret_val = g_swig_get_index_child (implementor, child_name); + } + + return ret_val; +} + +bool +ScriptInterpreterPython::UpdateSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + bool ret_val = false; + + if (!implementor_sp) + return ret_val; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return ret_val; + + if (!g_swig_update_provider) + return ret_val; + + { + Locker py_lock(this); + ret_val = g_swig_update_provider (implementor); + } + + return ret_val; +} + +bool +ScriptInterpreterPython::MightHaveChildrenSynthProviderInstance (const lldb::ScriptInterpreterObjectSP& implementor_sp) +{ + bool ret_val = false; + + if (!implementor_sp) + return ret_val; + + void* implementor = implementor_sp->GetObject(); + + if (!implementor) + return ret_val; + + if (!g_swig_mighthavechildren_provider) + return ret_val; + + { + Locker py_lock(this); + ret_val = g_swig_mighthavechildren_provider (implementor); + } + + return ret_val; +} + +static std::string +ReadPythonBacktrace (PyObject* py_backtrace) +{ + PyObject* traceback_module = NULL, + *stringIO_module = NULL, + *stringIO_builder = NULL, + *stringIO_buffer = NULL, + *printTB = NULL, + *printTB_args = NULL, + *printTB_result = NULL, + *stringIO_getvalue = NULL, + *printTB_string = NULL; + + std::string retval("backtrace unavailable"); + + if (py_backtrace && py_backtrace != Py_None) + { + traceback_module = PyImport_ImportModule("traceback"); + stringIO_module = PyImport_ImportModule("StringIO"); + + if (traceback_module && traceback_module != Py_None && stringIO_module && stringIO_module != Py_None) + { + stringIO_builder = PyObject_GetAttrString(stringIO_module, "StringIO"); + if (stringIO_builder && stringIO_builder != Py_None) + { + stringIO_buffer = PyObject_CallObject(stringIO_builder, NULL); + if (stringIO_buffer && stringIO_buffer != Py_None) + { + printTB = PyObject_GetAttrString(traceback_module, "print_tb"); + if (printTB && printTB != Py_None) + { + printTB_args = Py_BuildValue("OOO",py_backtrace,Py_None,stringIO_buffer); + printTB_result = PyObject_CallObject(printTB, printTB_args); + stringIO_getvalue = PyObject_GetAttrString(stringIO_buffer, "getvalue"); + if (stringIO_getvalue && stringIO_getvalue != Py_None) + { + printTB_string = PyObject_CallObject (stringIO_getvalue,NULL); + if (printTB_string && printTB_string != Py_None && PyString_Check(printTB_string)) + retval.assign(PyString_AsString(printTB_string)); + } + } + } + } + } + } + Py_XDECREF(traceback_module); + Py_XDECREF(stringIO_module); + Py_XDECREF(stringIO_builder); + Py_XDECREF(stringIO_buffer); + Py_XDECREF(printTB); + Py_XDECREF(printTB_args); + Py_XDECREF(printTB_result); + Py_XDECREF(stringIO_getvalue); + Py_XDECREF(printTB_string); + return retval; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + Process* process, + std::string& output, + Error& 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; + } + if (!g_swig_run_script_keyword_process) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + ProcessSP process_sp(process->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_process (impl_function, m_dictionary_name.c_str(), process_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + Thread* thread, + std::string& output, + Error& 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; + } + if (!g_swig_run_script_keyword_thread) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + ThreadSP thread_sp(thread->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_thread (impl_function, m_dictionary_name.c_str(), thread_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + Target* target, + std::string& output, + Error& 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; + } + if (!g_swig_run_script_keyword_target) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + TargetSP target_sp(target->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_target (impl_function, m_dictionary_name.c_str(), target_sp, output); + if (!ret_val) + error.SetErrorString("python script evaluation failed"); + } + return ret_val; +} + +bool +ScriptInterpreterPython::RunScriptFormatKeyword (const char* impl_function, + StackFrame* frame, + std::string& output, + Error& 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; + } + if (!g_swig_run_script_keyword_frame) + { + error.SetErrorString("internal helper function missing"); + return false; + } + { + StackFrameSP frame_sp(frame->shared_from_this()); + Locker py_lock(this); + ret_val = g_swig_run_script_keyword_frame (impl_function, m_dictionary_name.c_str(), frame_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 +ScriptInterpreterPython::LoadScriptingModule (const char* pathname, + bool can_reload, + bool init_session, + lldb_private::Error& error) +{ + if (!pathname || !pathname[0]) + { + error.SetErrorString("invalid pathname"); + return false; + } + + if (!g_swig_call_module_init) + { + error.SetErrorString("internal helper function missing"); + return false; + } + + lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); + + { + FileSpec target_file(pathname, true); + std::string basename(target_file.GetFilename().GetCString()); + + StreamString command_stream; + + // Before executing Pyton code, lock the GIL. + Locker py_lock (this, + Locker::AcquireLock | (init_session ? Locker::InitSession : 0), + Locker::FreeAcquiredLock | (init_session ? Locker::TearDownSession : 0)); + + if (target_file.GetFileType() == FileSpec::eFileTypeInvalid || + target_file.GetFileType() == FileSpec::eFileTypeUnknown) + { + // 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 (target_file.GetFileType() == FileSpec::eFileTypeDirectory || + target_file.GetFileType() == FileSpec::eFileTypeRegular || + target_file.GetFileType() == FileSpec::eFileTypeSymbolicLink) + { + std::string directory(target_file.GetDirectory().GetCString()); + 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)); + 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 (::strcmp(extension.GetCString(), "py") == 0) + basename.resize(basename.length()-3); + else if(::strcmp(extension.GetCString(), "pyc") == 0) + 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; + int refcount = 0; + // 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(), + ScriptInterpreterPython::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 = (ExecuteOneLineWithReturn(command_stream.GetData(), + ScriptInterpreterPython::eScriptReturnTypeInt, + &refcount, + ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false)) && refcount > 0); + + bool was_imported = (was_imported_globally || was_imported_locally); + + if (was_imported == true && can_reload == false) + { + 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(%s)",basename.c_str(),basename.c_str()); + else + command_stream.Printf("reload(%s)",basename.c_str()); + } + else + command_stream.Printf("import %s",basename.c_str()); + + bool import_retval = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false).SetMaskoutErrors(false)); + PyObject* py_error = PyErr_Occurred(); // per Python docs: "you do not need to Py_DECREF()" the return of this function + + if (py_error || !import_retval) // check for failure of the import + { + if (py_error) // if we have a Python error.. + { + PyObject *type = NULL,*value = NULL,*traceback = NULL; + PyErr_Fetch (&type,&value,&traceback); + + if (PyErr_GivenExceptionMatches (py_error, PyExc_ImportError)) // and it is an ImportError + { + if (value && value != Py_None) + error.SetErrorString(PyString_AsString(PyObject_Str(value))); + else + error.SetErrorString("ImportError raised by imported module"); + } + else // any other error + { + // get the backtrace + std::string bt = ReadPythonBacktrace(traceback); + + if (value && value != Py_None) + error.SetErrorStringWithFormat("Python error raised while importing module: %s - traceback: %s", PyString_AsString(PyObject_Str(value)),bt.c_str()); + else + error.SetErrorStringWithFormat("Python raised an error while importing module - traceback: %s",bt.c_str()); + } + + Py_XDECREF(type); + Py_XDECREF(value); + Py_XDECREF(traceback); + } + else // we failed but have no error to explain why + { + error.SetErrorString("unknown error while importing module"); + } + + // anyway, clear the error indicator and return false + PyErr_Clear(); + return false; + } + + // if we are here, everything worked + // call __lldb_init_module(debugger,dict) + if (!g_swig_call_module_init (basename.c_str(), + m_dictionary_name.c_str(), + debugger_sp)) + { + error.SetErrorString("calling __lldb_init_module failed"); + return false; + } + return true; + } +} + +lldb::ScriptInterpreterObjectSP +ScriptInterpreterPython::MakeScriptObject (void* object) +{ + return lldb::ScriptInterpreterObjectSP(new ScriptInterpreterPythonObject(object)); +} + +ScriptInterpreterPython::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); +} + +ScriptInterpreterPython::SynchronicityHandler::~SynchronicityHandler() +{ + if (m_synch_wanted != eScriptedCommandSynchronicityCurrentValue) + m_debugger_sp->SetAsyncExecution(m_old_asynch); +} + +bool +ScriptInterpreterPython::RunScriptBasedCommand(const char* impl_function, + const char* args, + ScriptedCommandSynchronicity synchronicity, + lldb_private::CommandReturnObject& cmd_retobj, + Error& error) +{ + if (!impl_function) + { + error.SetErrorString("no function to execute"); + return false; + } + + if (!g_swig_call_command) + { + error.SetErrorString("no helper function to run scripted commands"); + return false; + } + + lldb::DebuggerSP debugger_sp = m_interpreter.GetDebugger().shared_from_this(); + + 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, + Locker::FreeLock | Locker::TearDownSession); + + SynchronicityHandler synch_handler(debugger_sp, + synchronicity); + + // 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_command_thread_state = _PyThreadState_Current; + + PythonInputReaderManager py_input(this); + + ret_val = g_swig_call_command (impl_function, + m_dictionary_name.c_str(), + debugger_sp, + args, + cmd_retobj); + } + + 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 +ScriptInterpreterPython::GetDocumentationForItem(const char* item, std::string& dest) +{ + dest.clear(); + if (!item || !*item) + return false; + std::string command(item); + command += ".__doc__"; + + char* result_ptr = NULL; // 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.assign(str_stream.GetData()); + return false; + } +} + +std::unique_ptr +ScriptInterpreterPython::AcquireInterpreterLock () +{ + std::unique_ptr py_lock(new Locker(this, + Locker::AcquireLock | Locker::InitSession, + Locker::FreeLock | Locker::TearDownSession)); + return py_lock; +} + +void +ScriptInterpreterPython::InitializeInterpreter (SWIGInitCallback python_swig_init_callback) +{ + g_swig_init_callback = python_swig_init_callback; + g_swig_breakpoint_callback = LLDBSwigPythonBreakpointCallbackFunction; + g_swig_watchpoint_callback = LLDBSwigPythonWatchpointCallbackFunction; + g_swig_typescript_callback = LLDBSwigPythonCallTypeScript; + g_swig_synthetic_script = LLDBSwigPythonCreateSyntheticProvider; + g_swig_calc_children = LLDBSwigPython_CalculateNumChildren; + g_swig_get_child_index = LLDBSwigPython_GetChildAtIndex; + g_swig_get_index_child = LLDBSwigPython_GetIndexOfChildWithName; + g_swig_cast_to_sbvalue = LLDBSWIGPython_CastPyObjectToSBValue; + g_swig_update_provider = LLDBSwigPython_UpdateSynthProviderInstance; + g_swig_mighthavechildren_provider = LLDBSwigPython_MightHaveChildrenSynthProviderInstance; + g_swig_call_command = LLDBSwigPythonCallCommand; + g_swig_call_module_init = LLDBSwigPythonCallModuleInit; + g_swig_create_os_plugin = LLDBSWIGPythonCreateOSPlugin; + g_swig_run_script_keyword_process = LLDBSWIGPythonRunScriptKeywordProcess; + g_swig_run_script_keyword_thread = LLDBSWIGPythonRunScriptKeywordThread; + g_swig_run_script_keyword_target = LLDBSWIGPythonRunScriptKeywordTarget; + g_swig_run_script_keyword_frame = LLDBSWIGPythonRunScriptKeywordFrame; +} + +void +ScriptInterpreterPython::InitializePrivate () +{ + Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__); + + // Python will muck with STDIN terminal state, so save off any current TTY + // settings so we can restore them. + TerminalState stdin_tty_state; + stdin_tty_state.Save(STDIN_FILENO, false); + + PyGILState_STATE gstate; + Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE)); + bool threads_already_initialized = false; + if (PyEval_ThreadsInitialized ()) { + gstate = PyGILState_Ensure (); + if (log) + log->Printf("Ensured PyGILState. Previous state = %slocked\n", gstate == PyGILState_UNLOCKED ? "un" : ""); + threads_already_initialized = true; + } else { + // InitThreads acquires the GIL if it hasn't been called before. + PyEval_InitThreads (); + } + Py_InitializeEx (0); + + // Initialize SWIG after setting up python + assert (g_swig_init_callback != NULL); + g_swig_init_callback (); + + // Update the path python uses to search for modules to include the current directory. + + PyRun_SimpleString ("import sys"); + PyRun_SimpleString ("sys.path.append ('.')"); + + // Find the module that owns this code and use that path we get to + // set the sys.path appropriately. + + FileSpec file_spec; + char python_dir_path[PATH_MAX]; + if (Host::GetLLDBPath (ePathTypePythonDir, file_spec)) + { + std::string python_path("sys.path.insert(0,\""); + size_t orig_len = python_path.length(); + if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) + { + python_path.append (python_dir_path); + python_path.append ("\")"); + PyRun_SimpleString (python_path.c_str()); + python_path.resize (orig_len); + } + + if (Host::GetLLDBPath (ePathTypeLLDBShlibDir, file_spec)) + { + if (file_spec.GetPath(python_dir_path, sizeof (python_dir_path))) + { + python_path.append (python_dir_path); + python_path.append ("\")"); + PyRun_SimpleString (python_path.c_str()); + python_path.resize (orig_len); + } + } + } + + 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; from termios import *"); + + if (threads_already_initialized) { + if (log) + log->Printf("Releasing PyGILState. Returning to state = %slocked\n", gstate == PyGILState_UNLOCKED ? "un" : ""); + PyGILState_Release (gstate); + } else { + // We initialized the threads in this function, just unlock the GIL. + PyEval_SaveThread(); + } + + stdin_tty_state.Restore(); +} + +//void +//ScriptInterpreterPython::Terminate () +//{ +// // 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 +// // ScriptInterpreterPython::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). +// // +//// Py_Finalize (); +//} + +#endif // #ifdef LLDB_DISABLE_PYTHON diff --git a/source/Interpreter/embedded_interpreter.py b/source/Interpreter/embedded_interpreter.py new file mode 100644 index 00000000000..0e57c1e4aec --- /dev/null +++ b/source/Interpreter/embedded_interpreter.py @@ -0,0 +1,103 @@ +import readline +import code +import sys +import traceback + +class SimpleREPL(code.InteractiveConsole): + def __init__(self, prompt, dict): + code.InteractiveConsole.__init__(self,dict) + self.prompt = prompt + self.loop_exit = False + self.dict = dict + + def interact(self): + try: + sys.ps1 + except AttributeError: + sys.ps1 = ">>> " + try: + sys.ps2 + except AttributeError: + sys.ps2 = "... " + + while not self.loop_exit: + try: + self.read_py_command() + except (SystemExit, EOFError): + # EOF while in Python just breaks out to top level. + self.write('\n') + self.loop_exit = True + break + except KeyboardInterrupt: + self.write("\nKeyboardInterrupt\n") + self.resetbuffer() + more = 0 + except: + traceback.print_exc() + + def process_input (self, in_str): + # Canonicalize the format of the input string + temp_str = in_str + temp_str.strip(' \t') + words = temp_str.split() + temp_str = ('').join(words) + + # Check the input string to see if it was the quit + # command. If so, intercept it, so that it doesn't + # close stdin on us! + if (temp_str.lower() == "quit()" or temp_str.lower() == "exit()"): + self.loop_exit = True + in_str = "raise SystemExit " + return in_str + + def my_raw_input (self, prompt): + stream = sys.stdout + stream.write (prompt) + stream.flush () + try: + line = sys.stdin.readline() + except KeyboardInterrupt: + line = " \n" + except (SystemExit, EOFError): + line = "quit()\n" + if not line: + raise EOFError + if line[-1] == '\n': + line = line[:-1] + return line + + def read_py_command(self): + # Read off a complete Python command. + more = 0 + while 1: + if more: + prompt = sys.ps2 + else: + prompt = sys.ps1 + line = self.my_raw_input(prompt) + # Can be None if sys.stdin was redefined + encoding = getattr(sys.stdin, "encoding", None) + if encoding and not isinstance(line, unicode): + line = line.decode(encoding) + line = self.process_input (line) + more = self.push(line) + if not more: + break + + def one_line (self, input): + line = self.process_input (input) + more = self.push(line) + if more: + self.write ("Input not a complete line.") + self.resetbuffer() + more = 0 + +def run_python_interpreter (dict): + # Pass in the dictionary, for continuity from one session to the next. + repl = SimpleREPL('>>> ', dict) + repl.interact() + +def run_one_line (dict, input_string): + repl = SimpleREPL ('', dict) + repl.one_line (input_string) + diff --git a/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp new file mode 100644 index 00000000000..4685c3e759e --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.cpp @@ -0,0 +1,861 @@ +//===-- ABIMacOSX_arm.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_arm.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/ClangASTContext.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 "llvm/ADT/Triple.h" + +#include "Utility/ARM_DWARF_Registers.h" +#include "Utility/ARM_GCC_Registers.h" +#include "Plugins/Process/Utility/ARMDefines.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ========== ======= == === ============= ============ ======================= =================== =========================== ======================= ====================== ========== =============== + { "r0", "arg1", 4, 0, eEncodingUint , eFormatHex, { gcc_r0, dwarf_r0, LLDB_REGNUM_GENERIC_ARG1, gdb_arm_r0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r1", "arg2", 4, 0, eEncodingUint , eFormatHex, { gcc_r1, dwarf_r1, LLDB_REGNUM_GENERIC_ARG2, gdb_arm_r1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r2", "arg3", 4, 0, eEncodingUint , eFormatHex, { gcc_r2, dwarf_r2, LLDB_REGNUM_GENERIC_ARG3, gdb_arm_r2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r3", "arg4", 4, 0, eEncodingUint , eFormatHex, { gcc_r3, dwarf_r3, LLDB_REGNUM_GENERIC_ARG4, gdb_arm_r3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r4", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r4, dwarf_r4, LLDB_INVALID_REGNUM, gdb_arm_r4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r5", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r5, dwarf_r5, LLDB_INVALID_REGNUM, gdb_arm_r5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r6", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r6, dwarf_r6, LLDB_INVALID_REGNUM, gdb_arm_r6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r7", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r7, dwarf_r7, LLDB_REGNUM_GENERIC_FP, gdb_arm_r7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r8, dwarf_r8, LLDB_INVALID_REGNUM, gdb_arm_r8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r9, dwarf_r9, LLDB_INVALID_REGNUM, gdb_arm_r9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r10, dwarf_r10, LLDB_INVALID_REGNUM, gdb_arm_r10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r11, dwarf_r11, LLDB_INVALID_REGNUM, gdb_arm_r11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12", NULL, 4, 0, eEncodingUint , eFormatHex, { gcc_r12, dwarf_r12, LLDB_INVALID_REGNUM, gdb_arm_r12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "sp", "r13", 4, 0, eEncodingUint , eFormatHex, { gcc_sp, dwarf_sp, LLDB_REGNUM_GENERIC_SP, gdb_arm_sp, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "lr", "r14", 4, 0, eEncodingUint , eFormatHex, { gcc_lr, dwarf_lr, LLDB_REGNUM_GENERIC_RA, gdb_arm_lr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "pc", "r15", 4, 0, eEncodingUint , eFormatHex, { gcc_pc, dwarf_pc, LLDB_REGNUM_GENERIC_PC, gdb_arm_pc, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cpsr", "psr", 4, 0, eEncodingUint , eFormatHex, { gcc_cpsr, dwarf_cpsr, LLDB_REGNUM_GENERIC_FLAGS, gdb_arm_cpsr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s0", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s0, LLDB_INVALID_REGNUM, gdb_arm_s0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s1", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s1, LLDB_INVALID_REGNUM, gdb_arm_s1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s2", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s2, LLDB_INVALID_REGNUM, gdb_arm_s2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s3", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s3, LLDB_INVALID_REGNUM, gdb_arm_s3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s4", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s4, LLDB_INVALID_REGNUM, gdb_arm_s4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s5", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s5, LLDB_INVALID_REGNUM, gdb_arm_s5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s6", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s6, LLDB_INVALID_REGNUM, gdb_arm_s6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s7", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s7, LLDB_INVALID_REGNUM, gdb_arm_s7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s8", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s8, LLDB_INVALID_REGNUM, gdb_arm_s8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s9", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s9, LLDB_INVALID_REGNUM, gdb_arm_s9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s10", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s10, LLDB_INVALID_REGNUM, gdb_arm_s10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s11", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s11, LLDB_INVALID_REGNUM, gdb_arm_s11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s12", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s12, LLDB_INVALID_REGNUM, gdb_arm_s12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s13", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s13, LLDB_INVALID_REGNUM, gdb_arm_s13, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s14", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s14, LLDB_INVALID_REGNUM, gdb_arm_s14, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s15", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s15, LLDB_INVALID_REGNUM, gdb_arm_s15, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s16", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s16, LLDB_INVALID_REGNUM, gdb_arm_s16, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s17", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s17, LLDB_INVALID_REGNUM, gdb_arm_s17, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s18", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s18, LLDB_INVALID_REGNUM, gdb_arm_s18, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s19", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s19, LLDB_INVALID_REGNUM, gdb_arm_s19, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s20", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s20, LLDB_INVALID_REGNUM, gdb_arm_s20, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s21", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s21, LLDB_INVALID_REGNUM, gdb_arm_s21, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s22", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s22, LLDB_INVALID_REGNUM, gdb_arm_s22, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s23", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s23, LLDB_INVALID_REGNUM, gdb_arm_s23, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s24", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s24, LLDB_INVALID_REGNUM, gdb_arm_s24, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s25", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s25, LLDB_INVALID_REGNUM, gdb_arm_s25, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s26", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s26, LLDB_INVALID_REGNUM, gdb_arm_s26, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s27", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s27, LLDB_INVALID_REGNUM, gdb_arm_s27, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s28", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s28, LLDB_INVALID_REGNUM, gdb_arm_s28, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s29", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s29, LLDB_INVALID_REGNUM, gdb_arm_s29, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s30", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s30, LLDB_INVALID_REGNUM, gdb_arm_s30, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "s31", NULL, 4, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_s31, LLDB_INVALID_REGNUM, gdb_arm_s31, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fpscr", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM,LLDB_INVALID_REGNUM, gdb_arm_fpscr, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d0", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d0, LLDB_INVALID_REGNUM, gdb_arm_d0, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d1", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d1, LLDB_INVALID_REGNUM, gdb_arm_d1, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d2", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d2, LLDB_INVALID_REGNUM, gdb_arm_d2, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d3", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d3, LLDB_INVALID_REGNUM, gdb_arm_d3, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d4", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d4, LLDB_INVALID_REGNUM, gdb_arm_d4, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d5", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d5, LLDB_INVALID_REGNUM, gdb_arm_d5, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d6", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d6, LLDB_INVALID_REGNUM, gdb_arm_d6, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d7", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d7, LLDB_INVALID_REGNUM, gdb_arm_d7, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d8", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d8, LLDB_INVALID_REGNUM, gdb_arm_d8, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d9", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d9, LLDB_INVALID_REGNUM, gdb_arm_d9, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d10", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d10, LLDB_INVALID_REGNUM, gdb_arm_d10, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d11", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d11, LLDB_INVALID_REGNUM, gdb_arm_d11, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d12", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d12, LLDB_INVALID_REGNUM, gdb_arm_d12, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d13", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d13, LLDB_INVALID_REGNUM, gdb_arm_d13, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d14", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d14, LLDB_INVALID_REGNUM, gdb_arm_d14, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d15", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d15, LLDB_INVALID_REGNUM, gdb_arm_d15, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d16", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d16, LLDB_INVALID_REGNUM, gdb_arm_d16, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d17", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d17, LLDB_INVALID_REGNUM, gdb_arm_d17, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d18", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d18, LLDB_INVALID_REGNUM, gdb_arm_d18, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d19", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d19, LLDB_INVALID_REGNUM, gdb_arm_d19, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d20", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d20, LLDB_INVALID_REGNUM, gdb_arm_d20, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d21", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d21, LLDB_INVALID_REGNUM, gdb_arm_d21, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d22", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d22, LLDB_INVALID_REGNUM, gdb_arm_d22, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d23", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d23, LLDB_INVALID_REGNUM, gdb_arm_d23, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d24", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d24, LLDB_INVALID_REGNUM, gdb_arm_d24, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d25", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d25, LLDB_INVALID_REGNUM, gdb_arm_d25, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d26", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d26, LLDB_INVALID_REGNUM, gdb_arm_d26, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d27", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d27, LLDB_INVALID_REGNUM, gdb_arm_d27, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d28", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d28, LLDB_INVALID_REGNUM, gdb_arm_d28, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d29", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d29, LLDB_INVALID_REGNUM, gdb_arm_d29, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d30", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d30, LLDB_INVALID_REGNUM, gdb_arm_d30, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "d31", NULL, 8, 0, eEncodingIEEE754 , eFormatFloat, { LLDB_INVALID_REGNUM, dwarf_d31, LLDB_INVALID_REGNUM, gdb_arm_d31, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r8_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r9_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r10_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r11_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12_usr", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r12_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_usr", "sp_usr", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_usr", "lr_usr", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_usr, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r8_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r9_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r10_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r11_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12_fiq", NULL, 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r12_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_fiq", "sp_fiq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_fiq", "lr_fiq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_fiq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_irq", "sp_irq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_irq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_irq", "lr_irq", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_irq, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_abt", "sp_abt", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_abt, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_abt", "lr_abt", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_abt, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_und", "sp_und", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_und, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_und", "lr_und", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_und, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13_svc", "sp_svc", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r13_svc, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14_svc", "lr_svc", 4, 0, eEncodingUint , eFormatHex, { LLDB_INVALID_REGNUM, dwarf_r14_svc, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM, LLDB_INVALID_REGNUM }, NULL, NULL} +}; +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 * +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; iConvertRegisterKindToRegisterNumber (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; + + if (arg1_ptr) + { + reg_value.SetUInt32(*arg1_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r0"), reg_value)) + return false; + + if (arg2_ptr) + { + reg_value.SetUInt32(*arg2_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r1"), reg_value)) + return false; + + if (arg3_ptr) + { + reg_value.SetUInt32(*arg3_ptr); + if (!reg_ctx->WriteRegister (reg_ctx->GetRegisterInfoByName("r2"), reg_value)) + return false; + if (arg4_ptr) + { + reg_value.SetUInt32(*arg4_ptr); + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("r3"); + if (!reg_ctx->WriteRegister (reg_info, reg_value)) + return false; + if (arg5_ptr) + { + // Keep the stack 8 byte aligned, not that we need to + sp -= 8; + sp &= ~(8ull-1ull); + reg_value.SetUInt32(*arg5_ptr); + if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp, reg_info->byte_size, reg_value).Fail()) + return false; + if (arg6_ptr) + { + reg_value.SetUInt32(*arg6_ptr); + if (reg_ctx->WriteRegisterValueToMemory (reg_info, sp + 4, reg_info->byte_size, reg_value).Fail()) + return false; + } + } + } + } + } + } + + + 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->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 + + // 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; + + ClangASTType clang_type = value->GetClangType(); + if (clang_type) + { + bool is_signed = false; + size_t bit_width = 0; + if (clang_type.IsIntegerType (is_signed)) + { + bit_width = clang_type.GetBitSize(); + } + else if (clang_type.IsPointerOrReferenceType ()) + { + bit_width = clang_type.GetBitSize(); + } + 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 = NULL; + // 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; + Error error; + if (!exe_ctx.GetProcessRef().ReadScalarIntegerFromMemory(sp, arg_byte_size, is_signed, value->GetScalar(), error)) + return false; + + sp += arg_byte_size; + } + } + } + } + return true; +} + +ValueObjectSP +ABIMacOSX_arm::GetReturnValueObjectImpl (Thread &thread, + lldb_private::ClangASTType &clang_type) const +{ + Value value; + ValueObjectSP return_valobj_sp; + + if (!clang_type) + return return_valobj_sp; + + clang::ASTContext *ast_context = clang_type.GetASTContext(); + if (!ast_context) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, clang_type.GetOpaqueQualType()); + value.SetClangType (clang_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 (clang_type.IsIntegerType (is_signed)) + { + size_t bit_width = clang_type.GetBitSize(); + + switch (bit_width) + { + default: + return return_valobj_sp; + 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 (clang_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; +} + +Error +ABIMacOSX_arm::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_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 (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + 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 + { + error.SetErrorString("We don't support returning longer than 64 bit integer values at present."); + } + } + else if (clang_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) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t lr_reg_num = LLDB_INVALID_REGNUM; + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + lr_reg_num = dwarf_lr; + sp_reg_num = dwarf_sp; + pc_reg_num = dwarf_pc; + break; + + case eRegisterKindGeneric: + lr_reg_num = LLDB_REGNUM_GENERIC_RA; + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (lr_reg_num == LLDB_INVALID_REGNUM || + sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + // Our Call Frame Address is the stack pointer value + row->SetCFARegister (sp_reg_num); + + // 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) +{ + 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; + + unwind_plan.Clear (); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + row->SetCFARegister (fp_reg_num); + row->SetCFAOffset (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; +} + +// 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 include: r0, r1, r2, r3, r9, r12, r13 + 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[2] == '\0'; // r12 - r13 + 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; + } + 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': + switch (name[2]) + { + case '\0': + return true; // s2 is volatile + default: + break; + } + break; + + case '3': + switch (name[2]) + { + case '\0': + return true; // s3 is volatile + default: + break; + } + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return name[2] == '\0'; // s4 - s9 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/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h new file mode 100644 index 00000000000..27cea85aaf6 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-arm/ABIMacOSX_arm.h @@ -0,0 +1,138 @@ +//===-- ABIMacOSX_arm.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABIMacOSX_arm_h_ +#define liblldb_ABIMacOSX_arm_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABIMacOSX_arm : public lldb_private::ABI +{ +public: + ~ABIMacOSX_arm() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // 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; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // 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; + } + + virtual lldb::addr_t + FixCodeAddress (lldb::addr_t pc) + { + // 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; + } + + virtual bool + FunctionCallsChangeCFA () + { + return false; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: +private: + ABIMacOSX_arm() : + lldb_private::ABI() + { + // Call CreateInstance instead. + } +}; + +#endif // liblldb_ABIMacOSX_arm_h_ diff --git a/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp new file mode 100644 index 00000000000..deb531d937a --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.cpp @@ -0,0 +1,977 @@ +//===-- ABIMacOSX_i386.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABIMacOSX_i386.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Scalar.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Symbol/ClangASTContext.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 "llvm/ADT/Triple.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +enum +{ + gcc_eax = 0, + gcc_ecx, + gcc_edx, + gcc_ebx, + gcc_ebp, + gcc_esp, + gcc_esi, + gcc_edi, + gcc_eip, + gcc_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 +}; + +enum +{ + gdb_eax = 0, + gdb_ecx = 1, + gdb_edx = 2, + gdb_ebx = 3, + gdb_esp = 4, + gdb_ebp = 5, + gdb_esi = 6, + gdb_edi = 7, + gdb_eip = 8, + gdb_eflags = 9, + gdb_cs = 10, + gdb_ss = 11, + gdb_ds = 12, + gdb_es = 13, + gdb_fs = 14, + gdb_gs = 15, + gdb_stmm0 = 16, + gdb_stmm1 = 17, + gdb_stmm2 = 18, + gdb_stmm3 = 19, + gdb_stmm4 = 20, + gdb_stmm5 = 21, + gdb_stmm6 = 22, + gdb_stmm7 = 23, + gdb_fctrl = 24, gdb_fcw = gdb_fctrl, + gdb_fstat = 25, gdb_fsw = gdb_fstat, + gdb_ftag = 26, gdb_ftw = gdb_ftag, + gdb_fiseg = 27, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 28, gdb_ip = gdb_fioff, + gdb_foseg = 29, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 30, gdb_dp = gdb_fooff, + gdb_fop = 31, + gdb_xmm0 = 32, + gdb_xmm1 = 33, + gdb_xmm2 = 34, + gdb_xmm3 = 35, + gdb_xmm4 = 36, + gdb_xmm5 = 37, + gdb_xmm6 = 38, + gdb_xmm7 = 39, + gdb_mxcsr = 40, + gdb_mm0 = 41, + gdb_mm1 = 42, + gdb_mm2 = 43, + gdb_mm3 = 44, + gdb_mm4 = 45, + gdb_mm5 = 46, + gdb_mm6 = 47, + gdb_mm7 = 48, + gdb_ymm0 = gdb_xmm0, + gdb_ymm1 = gdb_xmm1, + gdb_ymm2 = gdb_xmm2, + gdb_ymm3 = gdb_xmm3, + gdb_ymm4 = gdb_xmm4, + gdb_ymm5 = gdb_xmm5, + gdb_ymm6 = gdb_xmm6, + gdb_ymm7 = gdb_xmm7 +}; + + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ====== ======= == === ============= ============ ===================== ===================== ============================ ==================== ====================== ========== =============== + { "eax", NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_eax , dwarf_eax , LLDB_INVALID_REGNUM , gdb_eax , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ebx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_ebx , dwarf_ebx , LLDB_INVALID_REGNUM , gdb_ebx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ecx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_ecx , dwarf_ecx , LLDB_REGNUM_GENERIC_ARG4 , gdb_ecx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "edx" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_edx , dwarf_edx , LLDB_REGNUM_GENERIC_ARG3 , gdb_edx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "esi" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_esi , dwarf_esi , LLDB_REGNUM_GENERIC_ARG2 , gdb_esi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "edi" , NULL, 4, 0, eEncodingUint , eFormatHex , { gcc_edi , dwarf_edi , LLDB_REGNUM_GENERIC_ARG1 , gdb_edi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ebp" , "fp", 4, 0, eEncodingUint , eFormatHex , { gcc_ebp , dwarf_ebp , LLDB_REGNUM_GENERIC_FP , gdb_ebp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "esp" , "sp", 4, 0, eEncodingUint , eFormatHex , { gcc_esp , dwarf_esp , LLDB_REGNUM_GENERIC_SP , gdb_esp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "eip" , "pc", 4, 0, eEncodingUint , eFormatHex , { gcc_eip , dwarf_eip , LLDB_REGNUM_GENERIC_PC , gdb_eip , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "eflags", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_eflags , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ss" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ds" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "es" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "gs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm0" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm0 , LLDB_INVALID_REGNUM , gdb_stmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm1" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm1 , LLDB_INVALID_REGNUM , gdb_stmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm2" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm2 , LLDB_INVALID_REGNUM , gdb_stmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm3" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm3 , LLDB_INVALID_REGNUM , gdb_stmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm4" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm4 , LLDB_INVALID_REGNUM , gdb_stmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm5" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm5 , LLDB_INVALID_REGNUM , gdb_stmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm6" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm6 , LLDB_INVALID_REGNUM , gdb_stmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm7" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_stmm7 , LLDB_INVALID_REGNUM , gdb_stmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fctrl" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fctrl , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fstat" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fstat , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ftag" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftag , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fiseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fiseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fioff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fioff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "foseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_foseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fooff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fooff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fop" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm0" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm0 , LLDB_INVALID_REGNUM , gdb_xmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm1" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm1 , LLDB_INVALID_REGNUM , gdb_xmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm2" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm2 , LLDB_INVALID_REGNUM , gdb_xmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm3" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm3 , LLDB_INVALID_REGNUM , gdb_xmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm4" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm4 , LLDB_INVALID_REGNUM , gdb_xmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm5" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm5 , LLDB_INVALID_REGNUM , gdb_xmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm6" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm6 , LLDB_INVALID_REGNUM , gdb_xmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm7" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_xmm7 , LLDB_INVALID_REGNUM , gdb_xmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "mxcsr" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm0" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm0 , LLDB_INVALID_REGNUM , gdb_ymm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm1" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm1 , LLDB_INVALID_REGNUM , gdb_ymm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm2" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm2 , LLDB_INVALID_REGNUM , gdb_ymm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm3" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm3 , LLDB_INVALID_REGNUM , gdb_ymm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm4" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm4 , LLDB_INVALID_REGNUM , gdb_ymm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm5" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm5 , LLDB_INVALID_REGNUM , gdb_ymm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm6" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm6 , LLDB_INVALID_REGNUM , gdb_ymm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm7" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { LLDB_INVALID_REGNUM , dwarf_ymm7 , LLDB_INVALID_REGNUM , gdb_ymm7 , LLDB_INVALID_REGNUM }, NULL, NULL} +}; + +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 * +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; iConvertRegisterKindToRegisterNumber (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 + + Error error; + RegisterValue reg_value; + + // Write any arguments onto the stack + if (arg1_ptr) + { + sp -= 4; + if (arg2_ptr) + { + sp -= 4; + if (arg3_ptr) + { + sp -= 4; + if (arg4_ptr) + { + sp -= 4; + if (arg5_ptr) + { + sp -= 4; + if (arg6_ptr) + { + sp -= 4; + } + } + } + } + } + } + + // Align the SP + sp &= ~(16ull-1ull); // 16-byte alignment + + if (arg1_ptr) + { + reg_value.SetUInt32(*arg1_ptr); + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg2_ptr) + { + reg_value.SetUInt32(*arg2_ptr); + // 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"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 4, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg3_ptr) + { + reg_value.SetUInt32(*arg3_ptr); + // 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"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 8, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + + if (arg4_ptr) + { + reg_value.SetUInt32(*arg4_ptr); + // 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"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 12, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + if (arg5_ptr) + { + reg_value.SetUInt32(*arg5_ptr); + // 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"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 16, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + if (arg6_ptr) + { + reg_value.SetUInt32(*arg6_ptr); + // 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"... + error = reg_ctx->WriteRegisterValueToMemory (reg_info_32, + sp + 20, + reg_info_32->byte_size, + reg_value); + if (error.Fail()) + return false; + } + } + } + } + } + } + + + // 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; +} + +bool +ABIMacOSX_i386::PrepareNormalCall (Thread &thread, + addr_t sp, + addr_t func_addr, + addr_t return_addr, + ValueList &args) const +{ + ExecutionContext exe_ctx (thread.shared_from_this()); + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + Process *process = exe_ctx.GetProcessPtr(); + Error error; + uint32_t fp_reg_num = reg_ctx->ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP); + 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); + + // Do the argument layout + + std::vector argLayout; // 4-byte chunks, as discussed in the ABI Function Call Guide + + size_t numArgs = args.GetSize(); + size_t index; + + for (index = 0; index < numArgs; ++index) + { + Value *val = args.GetValueAtIndex(index); + + if (!val) + return false; + + switch (val->GetValueType()) + { + case Value::eValueTypeScalar: + { + Scalar &scalar = val->GetScalar(); + switch (scalar.GetType()) + { + case Scalar::e_void: + 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: + { + uint64_t data = scalar.ULongLong(); + + switch (scalar.GetByteSize()) + { + default: + return false; + case 1: + argLayout.push_back((uint32_t)(data & 0xffull)); + break; + case 2: + argLayout.push_back((uint32_t)(data & 0xffffull)); + break; + case 4: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + break; + case 8: + argLayout.push_back((uint32_t)(data & 0xffffffffull)); + argLayout.push_back((uint32_t)(data >> 32)); + break; + } + } + break; + case Scalar::e_float: + { + float data = scalar.Float(); + uint32_t dataRaw = *((uint32_t*)(&data)); + argLayout.push_back(dataRaw); + } + break; + case Scalar::e_double: + { + double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + } + break; + case Scalar::e_long_double: + { + long double data = scalar.Double(); + uint32_t *dataRaw = ((uint32_t*)(&data)); + while ((argLayout.size() * 4) & 0xf) + argLayout.push_back(0); + argLayout.push_back(dataRaw[0]); + argLayout.push_back(dataRaw[1]); + argLayout.push_back(dataRaw[2]); + argLayout.push_back(dataRaw[3]); + } + break; + } + } + break; + case Value::eValueTypeHostAddress: + { + ClangASTType clang_type (val->GetClangType()); + if (clang_type) + { + uint32_t cstr_length = 0; + if (clang_type.IsCStringType (cstr_length)) + { + const char *cstr = (const char*)val->GetScalar().ULongLong(); + cstr_length = strlen(cstr); + + // Push the string onto the stack immediately. + + sp -= (cstr_length + 1); + + if (process->WriteMemory(sp, cstr, cstr_length + 1, error) != (cstr_length + 1)) + return false; + + // Put the address of the string into the argument array. + + argLayout.push_back((uint32_t)(sp & 0xffffffff)); + } + else + { + return false; + } + } + break; + } + break; + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + default: + return false; + } + } + + // Make room for the arguments on the stack + + sp -= 4 * argLayout.size(); + + // Align the SP + + sp &= ~(16ull-1ull); // 16-byte alignment + + // Write the arguments on the stack + + size_t numChunks = argLayout.size(); + + for (index = 0; index < numChunks; ++index) + if (process->WriteMemory(sp + (index * 4), &argLayout[index], sizeof(uint32_t), error) != sizeof(uint32_t)) + return false; + + // The return address is pushed onto the stack. + + sp -= 4; + uint32_t returnAddressU32 = return_addr; + if (process->WriteMemory (sp, &returnAddressU32, sizeof(returnAddressU32), error) != sizeof(returnAddressU32)) + return false; + + // %esp is set to the actual stack value. + + if (!reg_ctx->WriteRegisterFromUnsigned(sp_reg_num, sp)) + return false; + + // %ebp is set to a fake value, in our case 0x0x00000000 + + if (!reg_ctx->WriteRegisterFromUnsigned(fp_reg_num, 0x00000000)) + 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 ¤t_stack_argument) +{ + + uint32_t byte_size = (bit_width + (8-1))/8; + Error 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? + ClangASTType clang_type (value->GetClangType()); + if (clang_type) + { + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread.GetProcess().get(), + current_stack_argument); + } + else if (clang_type.IsPointerType()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread.GetProcess().get(), + current_stack_argument); + } + } + } + + return true; +} + +Error +ABIMacOSX_i386::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_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 (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + 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 (clang_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, + ClangASTType &clang_type) const +{ + Value value; + ValueObjectSP return_valobj_sp; + + if (!clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, clang_type.GetOpaqueQualType()); + value.SetClangType (clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + size_t bit_width = clang_type.GetBitSize(); + + 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 (clang_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; +} + +bool +ABIMacOSX_i386::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + sp_reg_num = dwarf_esp; + pc_reg_num = dwarf_eip; + break; + + case eRegisterKindGCC: + sp_reg_num = gcc_esp; + pc_reg_num = gcc_eip; + break; + + case eRegisterKindGDB: + sp_reg_num = gdb_esp; + pc_reg_num = gdb_eip; + break; + + case eRegisterKindGeneric: + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->SetCFARegister (sp_reg_num); + row->SetCFAOffset (4); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -4, false); + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("i386 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + return true; +} + +bool +ABIMacOSX_i386::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + 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; + + unwind_plan.Clear (); + unwind_plan.SetRegisterKind (eRegisterKindDWARF); + row->SetCFARegister (fp_reg_num); + row->SetCFAOffset (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->SetRegisterLocationToAtCFAPlusOffset(sp_reg_num, ptr_size * 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 + +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/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h new file mode 100644 index 00000000000..8c2d945e634 --- /dev/null +++ b/source/Plugins/ABI/MacOSX-i386/ABIMacOSX_i386.h @@ -0,0 +1,139 @@ +//===-- ABIMacOSX_i386.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABIMacOSX_i386_h_ +#define liblldb_ABIMacOSX_i386_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" +#include "lldb/Core/Value.h" + +class ABIMacOSX_i386 : + public lldb_private::ABI +{ +public: + + ~ABIMacOSX_i386() { } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t return_addr, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + PrepareNormalCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t func_addr, + lldb::addr_t return_addr, + lldb_private::ValueList &args) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // 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; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // Just make sure the address is a valid 32 bit address. + return pc <= UINT32_MAX; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + static lldb_private::ConstString + GetPluginNameStatic (); + + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABIMacOSX_i386() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + + +#endif // liblldb_ABI_h_ diff --git a/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp new file mode 100644 index 00000000000..a904d8b649c --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.cpp @@ -0,0 +1,1288 @@ +//===-- ABISysV_x86_64.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ABISysV_x86_64.h" + +#include "lldb/Core/ConstString.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/RegisterValue.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObjectConstResult.h" +#include "lldb/Core/ValueObjectRegister.h" +#include "lldb/Core/ValueObjectMemory.h" +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/UnwindPlan.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/Thread.h" + +#include "llvm/ADT/Triple.h" + +using namespace lldb; +using namespace lldb_private; + +enum gcc_dwarf_regnums +{ + gcc_dwarf_rax = 0, + gcc_dwarf_rdx, + gcc_dwarf_rcx, + gcc_dwarf_rbx, + gcc_dwarf_rsi, + gcc_dwarf_rdi, + gcc_dwarf_rbp, + gcc_dwarf_rsp, + gcc_dwarf_r8, + gcc_dwarf_r9, + gcc_dwarf_r10, + gcc_dwarf_r11, + gcc_dwarf_r12, + gcc_dwarf_r13, + gcc_dwarf_r14, + gcc_dwarf_r15, + gcc_dwarf_rip, + gcc_dwarf_xmm0, + gcc_dwarf_xmm1, + gcc_dwarf_xmm2, + gcc_dwarf_xmm3, + gcc_dwarf_xmm4, + gcc_dwarf_xmm5, + gcc_dwarf_xmm6, + gcc_dwarf_xmm7, + gcc_dwarf_xmm8, + gcc_dwarf_xmm9, + gcc_dwarf_xmm10, + gcc_dwarf_xmm11, + gcc_dwarf_xmm12, + gcc_dwarf_xmm13, + gcc_dwarf_xmm14, + gcc_dwarf_xmm15, + gcc_dwarf_stmm0, + gcc_dwarf_stmm1, + gcc_dwarf_stmm2, + gcc_dwarf_stmm3, + gcc_dwarf_stmm4, + gcc_dwarf_stmm5, + gcc_dwarf_stmm6, + gcc_dwarf_stmm7, + gcc_dwarf_ymm0, + gcc_dwarf_ymm1, + gcc_dwarf_ymm2, + gcc_dwarf_ymm3, + gcc_dwarf_ymm4, + gcc_dwarf_ymm5, + gcc_dwarf_ymm6, + gcc_dwarf_ymm7, + gcc_dwarf_ymm8, + gcc_dwarf_ymm9, + gcc_dwarf_ymm10, + gcc_dwarf_ymm11, + gcc_dwarf_ymm12, + gcc_dwarf_ymm13, + gcc_dwarf_ymm14, + gcc_dwarf_ymm15 +}; + +enum gdb_regnums +{ + gdb_rax = 0, + gdb_rbx = 1, + gdb_rcx = 2, + gdb_rdx = 3, + gdb_rsi = 4, + gdb_rdi = 5, + gdb_rbp = 6, + gdb_rsp = 7, + gdb_r8 = 8, + gdb_r9 = 9, + gdb_r10 = 10, + gdb_r11 = 11, + gdb_r12 = 12, + gdb_r13 = 13, + gdb_r14 = 14, + gdb_r15 = 15, + gdb_rip = 16, + gdb_rflags = 17, + gdb_cs = 18, + gdb_ss = 19, + gdb_ds = 20, + gdb_es = 21, + gdb_fs = 22, + gdb_gs = 23, + gdb_stmm0 = 24, + gdb_stmm1 = 25, + gdb_stmm2 = 26, + gdb_stmm3 = 27, + gdb_stmm4 = 28, + gdb_stmm5 = 29, + gdb_stmm6 = 30, + gdb_stmm7 = 31, + gdb_fctrl = 32, gdb_fcw = gdb_fctrl, + gdb_fstat = 33, gdb_fsw = gdb_fstat, + gdb_ftag = 34, gdb_ftw = gdb_ftag, + gdb_fiseg = 35, gdb_fpu_cs = gdb_fiseg, + gdb_fioff = 36, gdb_ip = gdb_fioff, + gdb_foseg = 37, gdb_fpu_ds = gdb_foseg, + gdb_fooff = 38, gdb_dp = gdb_fooff, + gdb_fop = 39, + gdb_xmm0 = 40, + gdb_xmm1 = 41, + gdb_xmm2 = 42, + gdb_xmm3 = 43, + gdb_xmm4 = 44, + gdb_xmm5 = 45, + gdb_xmm6 = 46, + gdb_xmm7 = 47, + gdb_xmm8 = 48, + gdb_xmm9 = 49, + gdb_xmm10 = 50, + gdb_xmm11 = 51, + gdb_xmm12 = 52, + gdb_xmm13 = 53, + gdb_xmm14 = 54, + gdb_xmm15 = 55, + gdb_mxcsr = 56, + gdb_ymm0 = 57, + gdb_ymm1 = 58, + gdb_ymm2 = 59, + gdb_ymm3 = 60, + gdb_ymm4 = 61, + gdb_ymm5 = 62, + gdb_ymm6 = 63, + gdb_ymm7 = 64, + gdb_ymm8 = 65, + gdb_ymm9 = 66, + gdb_ymm10 = 67, + gdb_ymm11 = 68, + gdb_ymm12 = 69, + gdb_ymm13 = 70, + gdb_ymm14 = 71, + gdb_ymm15 = 72 +}; + + +static RegisterInfo g_register_infos[] = +{ + // NAME ALT SZ OFF ENCODING FORMAT COMPILER DWARF GENERIC GDB LLDB NATIVE VALUE REGS INVALIDATE REGS + // ======== ======= == === ============= =================== ======================= ===================== =========================== ===================== ====================== ========== =============== + { "rax" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rax , gcc_dwarf_rax , LLDB_INVALID_REGNUM , gdb_rax , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rbx" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rbx , gcc_dwarf_rbx , LLDB_INVALID_REGNUM , gdb_rbx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rcx" , "arg4", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rcx , gcc_dwarf_rcx , LLDB_REGNUM_GENERIC_ARG4 , gdb_rcx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rdx" , "arg3", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rdx , gcc_dwarf_rdx , LLDB_REGNUM_GENERIC_ARG3 , gdb_rdx , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rsi" , "arg2", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rsi , gcc_dwarf_rsi , LLDB_REGNUM_GENERIC_ARG2 , gdb_rsi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rdi" , "arg1", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rdi , gcc_dwarf_rdi , LLDB_REGNUM_GENERIC_ARG1 , gdb_rdi , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rbp" , "fp", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rbp , gcc_dwarf_rbp , LLDB_REGNUM_GENERIC_FP , gdb_rbp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rsp" , "sp", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rsp , gcc_dwarf_rsp , LLDB_REGNUM_GENERIC_SP , gdb_rsp , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r8" , "arg5", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r8 , gcc_dwarf_r8 , LLDB_REGNUM_GENERIC_ARG5 , gdb_r8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r9" , "arg6", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r9 , gcc_dwarf_r9 , LLDB_REGNUM_GENERIC_ARG6 , gdb_r9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r10" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r10 , gcc_dwarf_r10 , LLDB_INVALID_REGNUM , gdb_r10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r11" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r11 , gcc_dwarf_r11 , LLDB_INVALID_REGNUM , gdb_r11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r12" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r12 , gcc_dwarf_r12 , LLDB_INVALID_REGNUM , gdb_r12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r13" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r13 , gcc_dwarf_r13 , LLDB_INVALID_REGNUM , gdb_r13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r14" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r14 , gcc_dwarf_r14 , LLDB_INVALID_REGNUM , gdb_r14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "r15" , NULL, 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_r15 , gcc_dwarf_r15 , LLDB_INVALID_REGNUM , gdb_r15 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rip" , "pc", 8, 0, eEncodingUint , eFormatHex , { gcc_dwarf_rip , gcc_dwarf_rip , LLDB_REGNUM_GENERIC_PC , gdb_rip , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "rflags", NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_REGNUM_GENERIC_FLAGS , gdb_rflags , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "cs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_cs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ss" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ss , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ds" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ds , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "es" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_es , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "gs" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_gs , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm0" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm0 , gcc_dwarf_stmm0 , LLDB_INVALID_REGNUM , gdb_stmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm1" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm1 , gcc_dwarf_stmm1 , LLDB_INVALID_REGNUM , gdb_stmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm2" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm2 , gcc_dwarf_stmm2 , LLDB_INVALID_REGNUM , gdb_stmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm3" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm3 , gcc_dwarf_stmm3 , LLDB_INVALID_REGNUM , gdb_stmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm4" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm4 , gcc_dwarf_stmm4 , LLDB_INVALID_REGNUM , gdb_stmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm5" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm5 , gcc_dwarf_stmm5 , LLDB_INVALID_REGNUM , gdb_stmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm6" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm6 , gcc_dwarf_stmm6 , LLDB_INVALID_REGNUM , gdb_stmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "stmm7" , NULL, 10, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_stmm7 , gcc_dwarf_stmm7 , LLDB_INVALID_REGNUM , gdb_stmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fctrl" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fctrl , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fstat" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fstat , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ftag" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_ftag , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fiseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fiseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fioff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fioff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "foseg" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_foseg , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fooff" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fooff , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "fop" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_fop , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm0" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm0 , gcc_dwarf_xmm0 , LLDB_INVALID_REGNUM , gdb_xmm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm1" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm1 , gcc_dwarf_xmm1 , LLDB_INVALID_REGNUM , gdb_xmm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm2" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm2 , gcc_dwarf_xmm2 , LLDB_INVALID_REGNUM , gdb_xmm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm3" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm3 , gcc_dwarf_xmm3 , LLDB_INVALID_REGNUM , gdb_xmm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm4" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm4 , gcc_dwarf_xmm4 , LLDB_INVALID_REGNUM , gdb_xmm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm5" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm5 , gcc_dwarf_xmm5 , LLDB_INVALID_REGNUM , gdb_xmm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm6" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm6 , gcc_dwarf_xmm6 , LLDB_INVALID_REGNUM , gdb_xmm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm7" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm7 , gcc_dwarf_xmm7 , LLDB_INVALID_REGNUM , gdb_xmm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm8" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm8 , gcc_dwarf_xmm8 , LLDB_INVALID_REGNUM , gdb_xmm8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm9" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm9 , gcc_dwarf_xmm9 , LLDB_INVALID_REGNUM , gdb_xmm9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm10" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm10 , gcc_dwarf_xmm10 , LLDB_INVALID_REGNUM , gdb_xmm10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm11" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm11 , gcc_dwarf_xmm11 , LLDB_INVALID_REGNUM , gdb_xmm11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm12" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm12 , gcc_dwarf_xmm12 , LLDB_INVALID_REGNUM , gdb_xmm12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm13" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm13 , gcc_dwarf_xmm13 , LLDB_INVALID_REGNUM , gdb_xmm13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm14" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm14 , gcc_dwarf_xmm14 , LLDB_INVALID_REGNUM , gdb_xmm14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "xmm15" , NULL, 16, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_xmm15 , gcc_dwarf_xmm15 , LLDB_INVALID_REGNUM , gdb_xmm15 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "mxcsr" , NULL, 4, 0, eEncodingUint , eFormatHex , { LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , LLDB_INVALID_REGNUM , gdb_mxcsr , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm0" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm0 , gcc_dwarf_ymm0 , LLDB_INVALID_REGNUM , gdb_ymm0 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm1" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm1 , gcc_dwarf_ymm1 , LLDB_INVALID_REGNUM , gdb_ymm1 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm2" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm2 , gcc_dwarf_ymm2 , LLDB_INVALID_REGNUM , gdb_ymm2 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm3" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm3 , gcc_dwarf_ymm3 , LLDB_INVALID_REGNUM , gdb_ymm3 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm4" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm4 , gcc_dwarf_ymm4 , LLDB_INVALID_REGNUM , gdb_ymm4 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm5" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm5 , gcc_dwarf_ymm5 , LLDB_INVALID_REGNUM , gdb_ymm5 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm6" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm6 , gcc_dwarf_ymm6 , LLDB_INVALID_REGNUM , gdb_ymm6 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm7" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm7 , gcc_dwarf_ymm7 , LLDB_INVALID_REGNUM , gdb_ymm7 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm8" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm8 , gcc_dwarf_ymm8 , LLDB_INVALID_REGNUM , gdb_ymm8 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm9" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm9 , gcc_dwarf_ymm9 , LLDB_INVALID_REGNUM , gdb_ymm9 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm10" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm10 , gcc_dwarf_ymm10 , LLDB_INVALID_REGNUM , gdb_ymm10 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm11" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm11 , gcc_dwarf_ymm11 , LLDB_INVALID_REGNUM , gdb_ymm11 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm12" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm12 , gcc_dwarf_ymm12 , LLDB_INVALID_REGNUM , gdb_ymm12 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm13" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm13 , gcc_dwarf_ymm13 , LLDB_INVALID_REGNUM , gdb_ymm13 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm14" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm14 , gcc_dwarf_ymm14 , LLDB_INVALID_REGNUM , gdb_ymm14 , LLDB_INVALID_REGNUM }, NULL, NULL}, + { "ymm15" , NULL, 32, 0, eEncodingVector, eFormatVectorOfUInt8, { gcc_dwarf_ymm15 , gcc_dwarf_ymm15 , LLDB_INVALID_REGNUM , gdb_ymm15 , LLDB_INVALID_REGNUM }, NULL, NULL} +}; + +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_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; iPutCString(s.GetString().c_str()); + } + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return false; + + const RegisterInfo *reg_info = NULL; + if (arg1_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rdi", 0); + if (log) + log->Printf("About to write arg1 (0x%" PRIx64 ") into %s", (uint64_t)*arg1_ptr, reg_info->name); + + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg1_ptr)) + return false; + + if (arg2_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rsi", 0); + if (log) + log->Printf("About to write arg2 (0x%" PRIx64 ") into %s", (uint64_t)*arg2_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg2_ptr)) + return false; + + if (arg3_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rdx", 0); + if (log) + log->Printf("About to write arg3 (0x%" PRIx64 ") into %s", (uint64_t)*arg3_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg3_ptr)) + return false; + + if (arg4_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("rcx", 0); + if (log) + log->Printf("About to write arg4 (0x%" PRIx64 ") into %s", (uint64_t)*arg4_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg4_ptr)) + return false; + + if (arg5_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("r8", 0); + if (log) + log->Printf("About to write arg5 (0x%" PRIx64 ") into %s", (uint64_t)*arg5_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg5_ptr)) + return false; + + if (arg6_ptr) + { + reg_info = reg_ctx->GetRegisterInfoByName("r9", 0); + if (log) + log->Printf("About to write arg6 (0x%" PRIx64 ") into %s", (uint64_t)*arg6_ptr, reg_info->name); + if (!reg_ctx->WriteRegisterFromUnsigned (reg_info, *arg6_ptr)) + 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 + + // The return address is pushed onto the stack (yes after the alignment...) + sp -= 8; + + RegisterValue reg_value; + reg_value.SetUInt64 (return_addr); + + if (log) + log->Printf("Pushing the return address onto the stack: new SP 0x%" PRIx64 ", return address 0x%" PRIx64, (uint64_t)sp, (uint64_t)return_addr); + + const RegisterInfo *pc_reg_info = reg_ctx->GetRegisterInfoByName("rip"); + Error error (reg_ctx->WriteRegisterValueToMemory(pc_reg_info, sp, pc_reg_info->byte_size, reg_value)); + if (error.Fail()) + return false; + + // %rsp is set to the actual stack value. + + if (log) + log->Printf("Writing SP (0x%" PRIx64 ") down", (uint64_t)sp); + + if (!reg_ctx->WriteRegisterFromUnsigned (reg_ctx->GetRegisterInfoByName("rsp"), sp)) + return false; + + // %rip is set to the address of the called function. + + if (log) + log->Printf("Writing new IP (0x%" PRIx64 ") down", (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 ¤t_argument_register, + addr_t ¤t_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; + Error 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->GetRegisterInfoByName("rdi", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[1] = reg_ctx->GetRegisterInfoByName("rsi", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[2] = reg_ctx->GetRegisterInfoByName("rdx", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[3] = reg_ctx->GetRegisterInfoByName("rcx", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[4] = reg_ctx->GetRegisterInfoByName("r8", 0)->kinds[eRegisterKindLLDB]; + argument_register_ids[5] = reg_ctx->GetRegisterInfoByName("r9", 0)->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? + ClangASTType clang_type = value->GetClangType(); + if (!clang_type) + return false; + bool is_signed; + + if (clang_type.IsIntegerType (is_signed)) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + is_signed, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + else if (clang_type.IsPointerType ()) + { + ReadIntegerArgument(value->GetScalar(), + clang_type.GetBitSize(), + false, + thread, + argument_register_ids, + current_argument_register, + current_stack_argument); + } + } + + return true; +} + +Error +ABISysV_x86_64::SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value_sp) +{ + Error error; + if (!new_value_sp) + { + error.SetErrorString("Empty value object for return value."); + return error; + } + + ClangASTType clang_type = new_value_sp->GetClangType(); + if (!clang_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 (clang_type.IsIntegerType (is_signed) || clang_type.IsPointerType()) + { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName("rax", 0); + + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + 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 (clang_type.IsFloatingPointType (count, is_complex)) + { + if (is_complex) + error.SetErrorString ("We don't support returning complex values at present"); + else + { + size_t bit_width = clang_type.GetBitSize(); + if (bit_width <= 64) + { + const RegisterInfo *xmm0_info = reg_ctx->GetRegisterInfoByName("xmm0", 0); + RegisterValue xmm0_value; + DataExtractor data; + size_t num_bytes = new_value_sp->GetData(data); + + 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, + ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + Value value; + + if (!return_clang_type) + return return_valobj_sp; + + //value.SetContext (Value::eContextTypeClangType, return_value_type); + value.SetClangType (return_clang_type); + + RegisterContext *reg_ctx = thread.GetRegisterContext().get(); + if (!reg_ctx) + return return_valobj_sp; + + const uint32_t type_flags = return_clang_type.GetTypeInfo (); + if (type_flags & ClangASTType::eTypeIsScalar) + { + value.SetValueType(Value::eValueTypeScalar); + + bool success = false; + if (type_flags & ClangASTType::eTypeIsInteger) + { + // Extract the register context so we can read arguments from registers + + const size_t byte_size = return_clang_type.GetByteSize(); + uint64_t raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(reg_ctx->GetRegisterInfoByName("rax", 0), 0); + const bool is_signed = (type_flags & ClangASTType::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 & ClangASTType::eTypeIsFloat) + { + if (type_flags & ClangASTType::eTypeIsComplex) + { + // Don't handle complex yet. + } + else + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (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 & ClangASTType::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 & ClangASTType::eTypeIsVector) + { + const size_t byte_size = return_clang_type.GetByteSize(); + if (byte_size > 0) + { + + const RegisterInfo *altivec_reg = reg_ctx->GetRegisterInfoByName("ymm0", 0); + if (altivec_reg == NULL) + { + altivec_reg = reg_ctx->GetRegisterInfoByName("xmm0", 0); + if (altivec_reg == NULL) + 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 heap_data_ap (new DataBufferHeap(byte_size, 0)); + const ByteOrder byte_order = process_sp->GetByteOrder(); + RegisterValue reg_value; + if (reg_ctx->ReadRegister(altivec_reg, reg_value)) + { + Error error; + if (reg_value.GetAsMemoryData (altivec_reg, + heap_data_ap->GetBytes(), + heap_data_ap->GetByteSize(), + byte_order, + error)) + { + DataExtractor data (DataBufferSP (heap_data_ap.release()), + byte_order, + process_sp->GetTarget().GetArchitecture().GetAddressByteSize()); + return_valobj_sp = ValueObjectConstResult::Create (&thread, + return_clang_type, + ConstString(""), + data); + } + } + } + } + } + } + } + + return return_valobj_sp; +} + +ValueObjectSP +ABISysV_x86_64::GetReturnValueObjectImpl (Thread &thread, ClangASTType &return_clang_type) const +{ + ValueObjectSP return_valobj_sp; + + if (!return_clang_type) + return return_valobj_sp; + + ExecutionContext exe_ctx (thread.shared_from_this()); + return_valobj_sp = GetReturnValueObjectSimple(thread, return_clang_type); + if (return_valobj_sp) + return return_valobj_sp; + + RegisterContextSP reg_ctx_sp = thread.GetRegisterContext(); + if (!reg_ctx_sp) + return return_valobj_sp; + + const size_t bit_width = return_clang_type.GetBitSize(); + if (return_clang_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 *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 + + const uint32_t num_children = return_clang_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; + + ClangASTType field_clang_type = return_clang_type.GetFieldAtIndex (idx, name, &field_bit_offset, NULL, NULL); + const size_t field_bit_width = field_clang_type.GetBitSize(); + + // 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 = NULL; + uint32_t copy_from_offset = 0; + + if (field_clang_type.IsIntegerType (is_signed) || field_clang_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 NULL return value object. + return return_valobj_sp; + } + } + else if (field_clang_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 + { + uint64_t next_field_bit_offset = 0; + ClangASTType next_field_clang_type = return_clang_type.GetFieldAtIndex (idx + 1, + name, + &next_field_bit_offset, + NULL, + NULL); + if (next_field_clang_type.IsIntegerType (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; + ClangASTType prev_field_clang_type = return_clang_type.GetFieldAtIndex (idx - 1, + name, + &prev_field_bit_offset, + NULL, + NULL); + if (prev_field_clang_type.IsIntegerType (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, + 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_clang_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, NULL), + return_clang_type); + } + } + + return return_valobj_sp; +} + +bool +ABISysV_x86_64::CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + sp_reg_num = gcc_dwarf_rsp; + pc_reg_num = gcc_dwarf_rip; + break; + + case eRegisterKindGDB: + sp_reg_num = gdb_rsp; + pc_reg_num = gdb_rip; + break; + + case eRegisterKindGeneric: + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + row->SetCFARegister (sp_reg_num); + row->SetCFAOffset (8); + row->SetRegisterLocationToAtCFAPlusOffset(pc_reg_num, -8, false); + unwind_plan.AppendRow (row); + unwind_plan.SetSourceName ("x86_64 at-func-entry default"); + unwind_plan.SetSourcedFromCompiler (eLazyBoolNo); + return true; +} + +bool +ABISysV_x86_64::CreateDefaultUnwindPlan (UnwindPlan &unwind_plan) +{ + uint32_t reg_kind = unwind_plan.GetRegisterKind(); + uint32_t fp_reg_num = LLDB_INVALID_REGNUM; + uint32_t sp_reg_num = LLDB_INVALID_REGNUM; + uint32_t pc_reg_num = LLDB_INVALID_REGNUM; + + switch (reg_kind) + { + case eRegisterKindDWARF: + case eRegisterKindGCC: + fp_reg_num = gcc_dwarf_rbp; + sp_reg_num = gcc_dwarf_rsp; + pc_reg_num = gcc_dwarf_rip; + break; + + case eRegisterKindGDB: + fp_reg_num = gdb_rbp; + sp_reg_num = gdb_rsp; + pc_reg_num = gdb_rip; + break; + + case eRegisterKindGeneric: + fp_reg_num = LLDB_REGNUM_GENERIC_FP; + sp_reg_num = LLDB_REGNUM_GENERIC_SP; + pc_reg_num = LLDB_REGNUM_GENERIC_PC; + break; + } + + if (fp_reg_num == LLDB_INVALID_REGNUM || + sp_reg_num == LLDB_INVALID_REGNUM || + pc_reg_num == LLDB_INVALID_REGNUM) + return false; + + UnwindPlan::RowSP row(new UnwindPlan::Row); + + const int32_t ptr_size = 8; + row->SetCFARegister (LLDB_REGNUM_GENERIC_FP); + row->SetCFAOffset (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->SetRegisterLocationToAtCFAPlusOffset(sp_reg_num, ptr_size * 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 + +bool +ABISysV_x86_64::RegisterIsCalleeSaved (const RegisterInfo *reg_info) +{ + if (reg_info) + { + // Preserved registers are : + // rbx, rsp, rbp, r12, r13, r14, r15 + // mxcsr (partially preserved) + // x87 control word + + const char *name = reg_info->name; + if (name[0] == 'r') + { + switch (name[1]) + { + case '1': // r12, r13, r14, r15 + if (name[2] >= '2' && name[2] <= '5') + return name[3] == '\0'; + break; + + default: + break; + } + } + + // Accept shorter-variant versions, rbx/ebx, rip/ eip, etc. + if (name[0] == 'r' || name[0] == 'e') + { + switch (name[1]) + { + case 'b': // rbp, rbx + if (name[2] == 'p' || name[2] == 'x') + return name[3] == '\0'; + break; + + case 'i': // rip + if (name[2] == 'p') + return name[3] == '\0'; + break; + + case 's': // rsp + if (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_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/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h new file mode 100644 index 00000000000..b10181960e8 --- /dev/null +++ b/source/Plugins/ABI/SysV-x86_64/ABISysV_x86_64.h @@ -0,0 +1,138 @@ +//===-- ABISysV_x86_64.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ABISysV_x86_64_h_ +#define liblldb_ABISysV_x86_64_h_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/lldb-private.h" +#include "lldb/Target/ABI.h" + +class ABISysV_x86_64 : + public lldb_private::ABI +{ +public: + + ~ABISysV_x86_64() + { + } + + virtual size_t + GetRedZoneSize () const; + + virtual bool + PrepareTrivialCall (lldb_private::Thread &thread, + lldb::addr_t sp, + lldb::addr_t functionAddress, + lldb::addr_t returnAddress, + lldb::addr_t *arg1_ptr = NULL, + lldb::addr_t *arg2_ptr = NULL, + lldb::addr_t *arg3_ptr = NULL, + lldb::addr_t *arg4_ptr = NULL, + lldb::addr_t *arg5_ptr = NULL, + lldb::addr_t *arg6_ptr = NULL) const; + + virtual bool + GetArgumentValues (lldb_private::Thread &thread, + lldb_private::ValueList &values) const; + + virtual lldb_private::Error + SetReturnValueObject(lldb::StackFrameSP &frame_sp, lldb::ValueObjectSP &new_value); + +protected: + lldb::ValueObjectSP + GetReturnValueObjectSimple (lldb_private::Thread &thread, + lldb_private::ClangASTType &ast_type) const; + +public: + virtual lldb::ValueObjectSP + GetReturnValueObjectImpl (lldb_private::Thread &thread, + lldb_private::ClangASTType &type) const; + + virtual bool + CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + CreateDefaultUnwindPlan (lldb_private::UnwindPlan &unwind_plan); + + virtual bool + RegisterIsVolatile (const lldb_private::RegisterInfo *reg_info); + + virtual bool + StackUsesFrames () + { + return true; + } + + virtual bool + CallFrameAddressIsValid (lldb::addr_t cfa) + { + // 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; + } + + virtual bool + CodeAddressIsValid (lldb::addr_t pc) + { + // We have a 64 bit address space, so anything is valid as opcodes + // aren't fixed width... + return true; + } + + virtual bool + FunctionCallsChangeCFA () + { + return true; + } + + virtual const lldb_private::RegisterInfo * + GetRegisterInfoArray (uint32_t &count); + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static lldb::ABISP + CreateInstance (const lldb_private::ArchSpec &arch); + + static lldb_private::ConstString + GetPluginNameStatic(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + void + CreateRegisterMapIfNeeded (); + + bool + RegisterIsCalleeSaved (const lldb_private::RegisterInfo *reg_info); + +private: + ABISysV_x86_64() : lldb_private::ABI() { } // Call CreateInstance instead. +}; + +#endif // liblldb_ABI_h_ diff --git a/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp new file mode 100644 index 00000000000..e920d70cd59 --- /dev/null +++ b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp @@ -0,0 +1,864 @@ +//===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DisassemblerLLVMC.h" + +#include "llvm-c/Disassembler.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCRelocationInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryObject.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include "llvm/ADT/SmallString.h" + + +#include "lldb/Core/Address.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/StackFrame.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +class InstructionLLVMC : public lldb_private::Instruction +{ +public: + InstructionLLVMC (DisassemblerLLVMC &disasm, + const lldb_private::Address &address, + AddressClass addr_class) : + Instruction (address, addr_class), + m_disasm_sp (disasm.shared_from_this()), + m_does_branch (eLazyBoolCalculate), + m_is_valid (false), + m_using_file_addr (false) + { + } + + virtual + ~InstructionLLVMC () + { + } + + virtual bool + DoesBranch () + { + if (m_does_branch == eLazyBoolCalculate) + { + GetDisassemblerLLVMC().Lock(this, NULL); + DataExtractor data; + if (m_opcode.GetData(data)) + { + bool is_alternate_isa; + lldb::addr_t pc = m_address.GetFileAddress(); + + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); + 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; + } + } + GetDisassemblerLLVMC().Unlock(); + } + return m_does_branch == eLazyBoolYes; + } + + DisassemblerLLVMC::LLVMCDisassembler * + GetDisasmToUse (bool &is_alternate_isa) + { + is_alternate_isa = false; + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + if (llvm_disasm.m_alternate_disasm_ap.get() != NULL) + { + const AddressClass address_class = GetAddressClass (); + + if (address_class == eAddressClassCodeAlternateISA) + { + is_alternate_isa = true; + return llvm_disasm.m_alternate_disasm_ap.get(); + } + } + return llvm_disasm.m_disasm_ap.get(); + } + + virtual size_t + Decode (const lldb_private::Disassembler &disassembler, + const lldb_private::DataExtractor &data, + lldb::offset_t data_offset) + { + // All we have to do is read the opcode which can be easy for some + // architectures + bool got_op = false; + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + const ArchSpec &arch = llvm_disasm.GetArchitecture(); + + 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)); + got_op = true; + break; + + case 2: + m_opcode.SetOpcode16 (data.GetU16 (&data_offset)); + got_op = true; + break; + + case 4: + m_opcode.SetOpcode32 (data.GetU32 (&data_offset)); + got_op = true; + break; + + case 8: + m_opcode.SetOpcode64 (data.GetU64 (&data_offset)); + 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::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); + + 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); + m_is_valid = true; + } + else + { + thumb_opcode <<= 16; + thumb_opcode |= data.GetU16(&data_offset); + m_opcode.SetOpcode16_2 (thumb_opcode); + m_is_valid = true; + } + } + else + { + m_opcode.SetOpcode32 (data.GetU32(&data_offset)); + 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(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; + + llvm_disasm.Lock(this, NULL); + const size_t inst_size = mc_disasm_ptr->GetMCInst(opcode_data, + opcode_data_len, + pc, + inst); + llvm_disasm.Unlock(); + if (inst_size == 0) + m_opcode.Clear(); + else + { + m_opcode.SetOpcodeBytes(opcode_data, inst_size); + m_is_valid = true; + } + } + } + return m_opcode.GetByteSize(); + } + + void + AppendComment (std::string &description) + { + if (m_comment.empty()) + m_comment.swap (description); + else + { + m_comment.append(", "); + m_comment.append(description); + } + } + + virtual void + CalculateMnemonicOperandsAndComment (const lldb_private::ExecutionContext *exe_ctx) + { + DataExtractor data; + const AddressClass address_class = GetAddressClass (); + + if (m_opcode.GetData(data)) + { + char out_string[512]; + + DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); + + DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr; + + if (address_class == eAddressClassCodeAlternateISA) + mc_disasm_ptr = llvm_disasm.m_alternate_disasm_ap.get(); + else + mc_disasm_ptr = llvm_disasm.m_disasm_ap.get(); + + lldb::addr_t pc = m_address.GetFileAddress(); + m_using_file_addr = true; + + const bool data_from_file = GetDisassemblerLLVMC().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; + } + } + } + } + + llvm_disasm.Lock(this, exe_ctx); + + 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, sizeof(out_string)); + } + + llvm_disasm.Unlock(); + + if (inst_size == 0) + { + m_comment.assign ("unknown opcode"); + inst_size = m_opcode.GetByteSize(); + StreamString mnemonic_strm; + lldb::offset_t offset = 0; + switch (inst_size) + { + case 1: + { + const uint8_t uval8 = data.GetU8 (&offset); + m_opcode.SetOpcode8 (uval8); + 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); + 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); + 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); + 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 == NULL) + 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; iCanBranch(inst); + if (can_branch) + m_does_branch = eLazyBoolYes; + else + m_does_branch = eLazyBoolNo; + + } + } + + if (!s_regex_compiled) + { + ::regcomp(&s_regex, "[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?", REG_EXTENDED); + s_regex_compiled = true; + } + + ::regmatch_t matches[3]; + + if (!::regexec(&s_regex, out_string, sizeof(matches) / sizeof(::regmatch_t), matches, 0)) + { + if (matches[1].rm_so != -1) + m_opcode_name.assign(out_string + matches[1].rm_so, matches[1].rm_eo - matches[1].rm_so); + if (matches[2].rm_so != -1) + m_mnemonics.assign(out_string + matches[2].rm_so, matches[2].rm_eo - matches[2].rm_so); + } + } + } + + bool + IsValid () const + { + return m_is_valid; + } + + bool + UsingFileAddress() const + { + return m_using_file_addr; + } + size_t + GetByteSize () const + { + return m_opcode.GetByteSize(); + } + + DisassemblerLLVMC & + GetDisassemblerLLVMC () + { + return *(DisassemblerLLVMC *)m_disasm_sp.get(); + } +protected: + + DisassemblerSP m_disasm_sp; // for ownership + LazyBool m_does_branch; + bool m_is_valid; + bool m_using_file_addr; + + static bool s_regex_compiled; + static ::regex_t s_regex; +}; + +bool InstructionLLVMC::s_regex_compiled = false; +::regex_t InstructionLLVMC::s_regex; + +DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner): + m_is_valid(true) +{ + std::string Error; + const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Error); + if (!curr_target) + { + m_is_valid = false; + return; + } + + m_instr_info_ap.reset(curr_target->createMCInstrInfo()); + m_reg_info_ap.reset (curr_target->createMCRegInfo(triple)); + + std::string features_str; + + m_subtarget_info_ap.reset(curr_target->createMCSubtargetInfo(triple, "", + features_str)); + + m_asm_info_ap.reset(curr_target->createMCAsmInfo(*curr_target->createMCRegInfo(triple), triple)); + + if (m_instr_info_ap.get() == NULL || m_reg_info_ap.get() == NULL || m_subtarget_info_ap.get() == NULL || m_asm_info_ap.get() == NULL) + { + m_is_valid = false; + return; + } + + m_context_ap.reset(new llvm::MCContext(m_asm_info_ap.get(), m_reg_info_ap.get(), 0)); + + m_disasm_ap.reset(curr_target->createMCDisassembler(*m_subtarget_info_ap.get())); + if (m_disasm_ap.get() && m_context_ap.get()) + { + llvm::OwningPtr RelInfo(curr_target->createMCRelocationInfo(triple, *m_context_ap.get())); + if (!RelInfo) + { + m_is_valid = false; + return; + } + m_disasm_ap->setupForSymbolicDisassembly(NULL, + DisassemblerLLVMC::SymbolLookupCallback, + (void *) &owner, + m_context_ap.get(), + RelInfo); + + unsigned asm_printer_variant; + if (flavor == ~0U) + asm_printer_variant = m_asm_info_ap->getAssemblerDialect(); + else + { + asm_printer_variant = flavor; + } + + m_instr_printer_ap.reset(curr_target->createMCInstPrinter(asm_printer_variant, + *m_asm_info_ap.get(), + *m_instr_info_ap.get(), + *m_reg_info_ap.get(), + *m_subtarget_info_ap.get())); + if (m_instr_printer_ap.get() == NULL) + { + m_disasm_ap.reset(); + m_is_valid = false; + } + } + else + m_is_valid = false; +} + +DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler() +{ +} + +namespace { + // This is the memory object we use in GetInstruction. + class LLDBDisasmMemoryObject : public llvm::MemoryObject { + const uint8_t *m_bytes; + uint64_t m_size; + uint64_t m_base_PC; + public: + LLDBDisasmMemoryObject(const uint8_t *bytes, uint64_t size, uint64_t basePC) : + m_bytes(bytes), m_size(size), m_base_PC(basePC) {} + + uint64_t getBase() const { return m_base_PC; } + uint64_t getExtent() const { return m_size; } + + int readByte(uint64_t addr, uint8_t *byte) const { + if (addr - m_base_PC >= m_size) + return -1; + *byte = m_bytes[addr - m_base_PC]; + return 0; + } + }; +} // End Anonymous Namespace + +uint64_t +DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data, + size_t opcode_data_len, + lldb::addr_t pc, + llvm::MCInst &mc_inst) +{ + LLDBDisasmMemoryObject memory_object (opcode_data, opcode_data_len, pc); + llvm::MCDisassembler::DecodeStatus status; + + uint64_t new_inst_size; + status = m_disasm_ap->getInstruction(mc_inst, + new_inst_size, + memory_object, + pc, + llvm::nulls(), + llvm::nulls()); + if (status == llvm::MCDisassembler::Success) + return new_inst_size; + else + return 0; +} + +uint64_t +DisassemblerLLVMC::LLVMCDisassembler::PrintMCInst (llvm::MCInst &mc_inst, + char *dst, + size_t dst_len) +{ + llvm::StringRef unused_annotations; + llvm::SmallString<64> inst_string; + llvm::raw_svector_ostream inst_stream(inst_string); + m_instr_printer_ap->printInst (&mc_inst, inst_stream, unused_annotations); + inst_stream.flush(); + const size_t output_size = std::min(dst_len - 1, inst_string.size()); + std::memcpy(dst, inst_string.data(), output_size); + dst[output_size] = '\0'; + + return output_size; +} + +void +DisassemblerLLVMC::LLVMCDisassembler::SetStyle (bool use_hex_immed, HexImmediateStyle hex_style) +{ + m_instr_printer_ap->setPrintImmHex(use_hex_immed); + switch(hex_style) + { + case eHexStyleC: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::C); break; + case eHexStyleAsm: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::Asm); break; + } +} + +bool +DisassemblerLLVMC::LLVMCDisassembler::CanBranch (llvm::MCInst &mc_inst) +{ + return m_instr_info_ap->get(mc_inst.getOpcode()).mayAffectControlFlow(mc_inst, *m_reg_info_ap.get()); +} + +bool +DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) +{ + llvm::Triple triple = arch.GetTriple(); + if (flavor == NULL || strcmp (flavor, "default") == 0) + return true; + + if (triple.getArch() == llvm::Triple::x86 || triple.getArch() == llvm::Triple::x86_64) + { + if (strcmp (flavor, "intel") == 0 || strcmp (flavor, "att") == 0) + return true; + else + return false; + } + else + return false; +} + + +Disassembler * +DisassemblerLLVMC::CreateInstance (const ArchSpec &arch, const char *flavor) +{ + if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) + { + std::unique_ptr disasm_ap (new DisassemblerLLVMC(arch, flavor)); + + if (disasm_ap.get() && disasm_ap->IsValid()) + return disasm_ap.release(); + } + return NULL; +} + +DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) : + Disassembler(arch, flavor_string), + m_exe_ctx (NULL), + m_inst (NULL), + m_data_from_file (false) +{ + if (!FlavorValidForArchSpec (arch, m_flavor.c_str())) + { + m_flavor.assign("default"); + } + + const char *triple = arch.GetTriple().getTriple().c_str(); + unsigned flavor = ~0U; + + // So far the only supported flavor is "intel" on x86. The base class will set this + // correctly coming in. + if (arch.GetTriple().getArch() == llvm::Triple::x86 + || arch.GetTriple().getArch() == llvm::Triple::x86_64) + { + if (m_flavor == "intel") + { + flavor = 1; + } + else if (m_flavor == "att") + { + flavor = 0; + } + } + + ArchSpec thumb_arch(arch); + if (arch.GetTriple().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 = "thumbv7"; + } + thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name.c_str())); + } + + // Cortex-M3 devices (e.g. armv7m) can only execute thumb (T2) instructions, + // so hardcode the primary disassembler to thumb mode. + if (arch.GetTriple().getArch() == llvm::Triple::arm + && (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m || arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em)) + { + triple = thumb_arch.GetTriple().getTriple().c_str(); + } + + m_disasm_ap.reset (new LLVMCDisassembler(triple, flavor, *this)); + if (!m_disasm_ap->IsValid()) + { + // We use m_disasm_ap.get() to tell whether we are valid or not, so if this isn't good for some reason, + // we reset it, and then we won't be valid and FindPlugin will fail and we won't get used. + m_disasm_ap.reset(); + } + + // For arm CPUs that can execute arm or thumb instructions, also create a thumb instruction disassembler. + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::string thumb_triple(thumb_arch.GetTriple().getTriple()); + m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), flavor, *this)); + if (!m_alternate_disasm_ap->IsValid()) + { + m_disasm_ap.reset(); + m_alternate_disasm_ap.reset(); + } + } +} + +DisassemblerLLVMC::~DisassemblerLLVMC() +{ +} + +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 = eAddressClassCode; + + if (m_alternate_disasm_ap.get() != NULL) + 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 and ARM.", + 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(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(disassembler)->SymbolLookup(value, + type, + pc, + name); +} + +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: + bzero (tag_bug, 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() : NULL; + Address value_so_addr; + if (m_inst->UsingFileAddress()) + { + ModuleSP module_sp(m_inst->GetAddress().GetModule()); + if (module_sp) + module_sp->ResolveFileAddress(value, value_so_addr); + } + else if (target && !target->GetSectionLoadList().IsEmpty()) + { + target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr); + } + + if (value_so_addr.IsValid() && value_so_addr.GetSection()) + { + StreamString ss; + + value_so_addr.Dump (&ss, + target, + Address::DumpStyleResolvedDescriptionNoModule, + Address::DumpStyleSectionNameOffset); + + if (!ss.GetString().empty()) + { + m_inst->AppendComment(ss.GetString()); + } + } + } + } + + *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; + *name = NULL; + return NULL; +} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +ConstString +DisassemblerLLVMC::GetPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +DisassemblerLLVMC::GetPluginVersion() +{ + return 1; +} + diff --git a/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h new file mode 100644 index 00000000000..c567791866d --- /dev/null +++ b/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.h @@ -0,0 +1,166 @@ +//===-- DisassemblerLLVMC.h -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DisassemblerLLVMC_h_ +#define liblldb_DisassemblerLLVMC_h_ + +#include + +#include "llvm-c/Disassembler.h" + +// Opaque references to C++ Objects in LLVM's MC. +namespace llvm +{ + class MCContext; + class MCInst; + class MCInstrInfo; + class MCRegisterInfo; + class MCDisassembler; + class MCInstPrinter; + class MCAsmInfo; + class MCSubtargetInfo; +} + +#include "lldb/Core/Address.h" +#include "lldb/Core/Disassembler.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Host/Mutex.h" + +class InstructionLLVMC; + +class DisassemblerLLVMC : public lldb_private::Disassembler +{ + // 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, I added this class to manage the actual disassemblers. + class LLVMCDisassembler + { + public: + LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner); + + ~LLVMCDisassembler(); + + uint64_t GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst); + uint64_t PrintMCInst (llvm::MCInst &mc_inst, char *output_buffer, size_t out_buffer_len); + void SetStyle (bool use_hex_immed, HexImmediateStyle hex_style); + bool CanBranch (llvm::MCInst &mc_inst); + bool IsValid() + { + return m_is_valid; + } + + private: + bool m_is_valid; + std::unique_ptr m_context_ap; + std::unique_ptr m_asm_info_ap; + std::unique_ptr m_subtarget_info_ap; + std::unique_ptr m_instr_info_ap; + std::unique_ptr m_reg_info_ap; + std::unique_ptr m_instr_printer_ap; + std::unique_ptr m_disasm_ap; + }; + +public: + //------------------------------------------------------------------ + // 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); + + DisassemblerLLVMC(const lldb_private::ArchSpec &arch, const char *flavor /* = NULL */); + + virtual + ~DisassemblerLLVMC(); + + virtual 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); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +protected: + friend class InstructionLLVMC; + + virtual bool + FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor); + + bool + IsValid() + { + return (m_disasm_ap.get() != NULL && m_disasm_ap->IsValid()); + } + + 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); + + void Lock(InstructionLLVMC *inst, + const lldb_private::ExecutionContext *exe_ctx) + { + m_mutex.Lock(); + m_inst = inst; + m_exe_ctx = exe_ctx; + } + + void Unlock() + { + m_inst = NULL; + m_exe_ctx = NULL; + m_mutex.Unlock(); + } + + const lldb_private::ExecutionContext *m_exe_ctx; + InstructionLLVMC *m_inst; + lldb_private::Mutex m_mutex; + bool m_data_from_file; + + std::unique_ptr m_disasm_ap; + std::unique_ptr m_alternate_disasm_ap; +}; + +#endif // liblldb_DisassemblerLLVM_h_ diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp new file mode 100644 index 00000000000..2604ae67016 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.cpp @@ -0,0 +1,177 @@ +//===-- AuxVector.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include +#include +#include + +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/DataExtractor.h" +#include "lldb/Core/Log.h" +#include "lldb/Target/Process.h" + +#if defined(__linux__) or defined(__FreeBSD__) +#include "Plugins/Process/elf-core/ProcessElfCore.h" +#endif + +#include "AuxVector.h" + +using namespace lldb; +using namespace lldb_private; + +static bool +GetMaxU64(DataExtractor &data, + lldb::offset_t *offset_ptr, + uint64_t *value, + unsigned int byte_size) +{ + lldb::offset_t saved_offset = *offset_ptr; + *value = data.GetMaxU64(offset_ptr, byte_size); + return *offset_ptr != saved_offset; +} + +static bool +ParseAuxvEntry(DataExtractor &data, + AuxVector::Entry &entry, + lldb::offset_t *offset_ptr, + unsigned int byte_size) +{ + if (!GetMaxU64(data, offset_ptr, &entry.type, byte_size)) + return false; + + if (!GetMaxU64(data, offset_ptr, &entry.value, byte_size)) + return false; + + return true; +} + +DataBufferSP +AuxVector::GetAuxvData() +{ +#if defined(__linux__) or defined(__FreeBSD__) + if (m_process->GetPluginName() == ProcessElfCore::GetPluginNameStatic()) + return static_cast(m_process)->GetAuxvData(); +#endif + return lldb_private::Host::GetAuxvData(m_process); +} + +void +AuxVector::ParseAuxv(DataExtractor &data) +{ + const unsigned int byte_size = m_process->GetAddressByteSize(); + lldb::offset_t offset = 0; + + for (;;) + { + Entry entry; + + if (!ParseAuxvEntry(data, entry, &offset, byte_size)) + break; + + if (entry.type == AT_NULL) + break; + + if (entry.type == AT_IGNORE) + continue; + + m_auxv.push_back(entry); + } +} + +AuxVector::AuxVector(Process *process) + : m_process(process) +{ + DataExtractor data; + Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_DYNAMIC_LOADER)); + + data.SetData(GetAuxvData()); + data.SetByteOrder(m_process->GetByteOrder()); + data.SetAddressByteSize(m_process->GetAddressByteSize()); + + ParseAuxv(data); + + if (log) + DumpToLog(log); +} + +AuxVector::iterator +AuxVector::FindEntry(EntryType type) const +{ + for (iterator I = begin(); I != end(); ++I) + { + if (I->type == static_cast(type)) + return I; + } + + return end(); +} + +void +AuxVector::DumpToLog(Log *log) const +{ + if (!log) + return; + + log->PutCString("AuxVector: "); + for (iterator I = begin(); I != end(); ++I) + { + log->Printf(" %s [%" PRIu64 "]: %" PRIx64, GetEntryName(*I), I->type, I->value); + } +} + +const char * +AuxVector::GetEntryName(EntryType type) +{ + const char *name = "AT_???"; + +#define ENTRY_NAME(_type) _type: name = #_type + switch (type) + { + case ENTRY_NAME(AT_NULL); break; + case ENTRY_NAME(AT_IGNORE); break; + case ENTRY_NAME(AT_EXECFD); break; + case ENTRY_NAME(AT_PHDR); break; + case ENTRY_NAME(AT_PHENT); break; + case ENTRY_NAME(AT_PHNUM); break; + case ENTRY_NAME(AT_PAGESZ); break; + case ENTRY_NAME(AT_BASE); break; + case ENTRY_NAME(AT_FLAGS); break; + case ENTRY_NAME(AT_ENTRY); break; + case ENTRY_NAME(AT_NOTELF); break; + case ENTRY_NAME(AT_UID); break; + case ENTRY_NAME(AT_EUID); break; + case ENTRY_NAME(AT_GID); break; + case ENTRY_NAME(AT_EGID); break; + case ENTRY_NAME(AT_CLKTCK); break; + case ENTRY_NAME(AT_PLATFORM); break; + case ENTRY_NAME(AT_HWCAP); break; + case ENTRY_NAME(AT_FPUCW); break; + case ENTRY_NAME(AT_DCACHEBSIZE); break; + case ENTRY_NAME(AT_ICACHEBSIZE); break; + case ENTRY_NAME(AT_UCACHEBSIZE); break; + case ENTRY_NAME(AT_IGNOREPPC); break; + case ENTRY_NAME(AT_SECURE); break; + case ENTRY_NAME(AT_BASE_PLATFORM); break; + case ENTRY_NAME(AT_RANDOM); break; + case ENTRY_NAME(AT_EXECFN); break; + case ENTRY_NAME(AT_SYSINFO); break; + case ENTRY_NAME(AT_SYSINFO_EHDR); break; + case ENTRY_NAME(AT_L1I_CACHESHAPE); break; + case ENTRY_NAME(AT_L1D_CACHESHAPE); break; + case ENTRY_NAME(AT_L2_CACHESHAPE); break; + case ENTRY_NAME(AT_L3_CACHESHAPE); break; + } +#undef ENTRY_NAME + + return name; +} + diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h new file mode 100644 index 00000000000..2d39eddcacc --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/AuxVector.h @@ -0,0 +1,115 @@ +//===-- AuxVector.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_AuxVector_H_ +#define liblldb_AuxVector_H_ + +// C Includes +// C++ Includes +#include + +// Other libraries and framework includes +#include "lldb/lldb-forward.h" + +namespace lldb_private { +class DataExtractor; +} + +/// @class AuxVector +/// @brief Represents a processes auxiliary vector. +/// +/// When a process is loaded on Linux a vector of values is placed onto the +/// stack communicating operating system specific information. On construction +/// this class locates and parses this information and provides a simple +/// read-only interface to the entries found. +class AuxVector { + +public: + AuxVector(lldb_private::Process *process); + + struct Entry { + uint64_t type; + uint64_t value; + + Entry() : type(0), value(0) { } + }; + + /// Constants describing the type of entry. + /// On Linux, running "LD_SHOW_AUXV=1 ./executable" will spew AUX information. + enum EntryType { + AT_NULL = 0, ///< End of auxv. + AT_IGNORE = 1, ///< Ignore entry. + AT_EXECFD = 2, ///< File descriptor of program. + AT_PHDR = 3, ///< Program headers. + AT_PHENT = 4, ///< Size of program header. + AT_PHNUM = 5, ///< Number of program headers. + AT_PAGESZ = 6, ///< Page size. + AT_BASE = 7, ///< Interpreter base address. + AT_FLAGS = 8, ///< Flags. + AT_ENTRY = 9, ///< Program entry point. + AT_NOTELF = 10, ///< Set if program is not an ELF. + AT_UID = 11, ///< UID. + AT_EUID = 12, ///< Effective UID. + AT_GID = 13, ///< GID. + AT_EGID = 14, ///< Effective GID. + AT_CLKTCK = 17, ///< Clock frequency (e.g. times(2)). + AT_PLATFORM = 15, ///< String identifying platform. + AT_HWCAP = 16, ///< Machine dependent hints about processor capabilities. + AT_FPUCW = 18, ///< Used FPU control word. + AT_DCACHEBSIZE = 19, ///< Data cache block size. + AT_ICACHEBSIZE = 20, ///< Instruction cache block size. + AT_UCACHEBSIZE = 21, ///< Unified cache block size. + AT_IGNOREPPC = 22, ///< Entry should be ignored. + AT_SECURE = 23, ///< Boolean, was exec setuid-like? + AT_BASE_PLATFORM = 24, ///< String identifying real platforms. + AT_RANDOM = 25, ///< Address of 16 random bytes. + AT_EXECFN = 31, ///< Filename of executable. + AT_SYSINFO = 32, ///< Pointer to the global system page used for system calls and other nice things. + AT_SYSINFO_EHDR = 33, + AT_L1I_CACHESHAPE = 34, ///< Shapes of the caches. + AT_L1D_CACHESHAPE = 35, + AT_L2_CACHESHAPE = 36, + AT_L3_CACHESHAPE = 37, + }; + +private: + typedef std::vector EntryVector; + +public: + typedef EntryVector::const_iterator iterator; + + iterator begin() const { return m_auxv.begin(); } + iterator end() const { return m_auxv.end(); } + + iterator + FindEntry(EntryType type) const; + + static const char * + GetEntryName(const Entry &entry) { + return GetEntryName(static_cast(entry.type)); + } + + static const char * + GetEntryName(EntryType type); + + void + DumpToLog(lldb_private::Log *log) const; + +private: + lldb_private::Process *m_process; + EntryVector m_auxv; + + lldb::DataBufferSP + GetAuxvData(); + + void + ParseAuxv(lldb_private::DataExtractor &data); +}; + +#endif diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp new file mode 100644 index 00000000000..3e1b52938f4 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.cpp @@ -0,0 +1,336 @@ +//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/Target.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) +{ + addr_t info_location; + addr_t info_addr; + Error error; + size_t size; + + info_location = process->GetImageInfoAddress(); + + if (info_location == LLDB_INVALID_ADDRESS) + return LLDB_INVALID_ADDRESS; + + info_addr = 0; + size = process->DoReadMemory(info_location, &info_addr, + process->GetAddressByteSize(), error); + if (size != process->GetAddressByteSize() || error.Fail()) + return LLDB_INVALID_ADDRESS; + + if (info_addr == 0) + 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_soentries(), + m_added_soentries(), + m_removed_soentries() +{ + // 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 +DYLDRendezvous::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 = ReadMemory(cursor, &info.version, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.map_addr, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.brk, address_size))) + return false; + + if (!(cursor = ReadMemory(cursor, &info.state, word_size))) + return false; + + if (!(cursor = ReadMemory(cursor + padding, &info.ldbase, address_size))) + 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(); +} + +bool +DYLDRendezvous::IsValid() +{ + return m_rendezvous_addr != LLDB_INVALID_ADDRESS; +} + +bool +DYLDRendezvous::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) + { + 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 +DYLDRendezvous::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 +DYLDRendezvous::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 +DYLDRendezvous::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 +DYLDRendezvous::ReadMemory(addr_t addr, void *dst, size_t size) +{ + size_t bytes_read; + Error error; + + bytes_read = m_process->DoReadMemory(addr, dst, size, error); + if (bytes_read != size || error.Fail()) + return 0; + + return addr + bytes_read; +} + +std::string +DYLDRendezvous::ReadStringFromMemory(addr_t addr) +{ + std::string str; + Error 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 +DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry) +{ + size_t address_size = m_process->GetAddressByteSize(); + + entry.clear(); + + if (!(addr = ReadMemory(addr, &entry.base_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.path_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.dyn_addr, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.next, address_size))) + return false; + + if (!(addr = ReadMemory(addr, &entry.prev, address_size))) + return false; + + entry.path = ReadStringFromMemory(entry.path_addr); + + return true; +} + +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->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/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h new file mode 100644 index 00000000000..67e7228a38d --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DYLDRendezvous.h @@ -0,0 +1,230 @@ +//===-- DYLDRendezvous.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_Rendezvous_H_ +#define liblldb_Rendezvous_H_ + +// C Includes +// C++ Includes +#include +#include + +// Other libraries and framework includes +#include "lldb/lldb-defines.h" +#include "lldb/lldb-types.h" + +namespace lldb_private { +class Process; +} + +/// @class DYLDRendezvous +/// @brief 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: + 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 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; + + /// @brief Constants describing the state of the rendezvous. + /// + /// @see GetState(). + enum RendezvousState { + eConsistent, + eAdd, + eDelete + }; + + /// @brief 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 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() { + base_addr = 0; + path_addr = 0; + dyn_addr = 0; + next = 0; + prev = 0; + path.clear(); + } + }; + +protected: + typedef std::list 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; + + /// Reads @p size bytes from the inferiors address space starting at @p + /// addr. + /// + /// @returns addr + size if the read was successful and false otherwise. + lldb::addr_t + ReadMemory(lldb::addr_t addr, void *dst, size_t size); + + /// 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); +}; + +#endif diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp new file mode 100644 index 00000000000..91c7cd3dfca --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.cpp @@ -0,0 +1,481 @@ +//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/ModuleSpec.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/Breakpoint/BreakpointLocation.h" + +#include "AuxVector.h" +#include "DynamicLoaderPOSIXDYLD.h" + +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."; +} + +void +DynamicLoaderPOSIXDYLD::GetPluginCommandHelp(const char *command, Stream *strm) +{ +} + +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::Linux || + triple_ref.getOS() == llvm::Triple::FreeBSD) + create = true; + } + + if (create) + return new DynamicLoaderPOSIXDYLD (process); + return NULL; +} + +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) +{ +} + +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() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + LoadAllCurrentModules(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +void +DynamicLoaderPOSIXDYLD::DidLaunch() +{ + ModuleSP executable; + addr_t load_offset; + + m_auxv.reset(new AuxVector(m_process)); + + executable = GetTargetExecutable(); + load_offset = ComputeLoadOffset(); + + if (executable.get() && load_offset != LLDB_INVALID_ADDRESS) + { + ModuleList module_list; + module_list.Append(executable); + UpdateLoadedSections(executable, load_offset); + ProbeEntry(); + m_process->GetTarget().ModulesDidLoad(module_list); + } +} + +ModuleSP +DynamicLoaderPOSIXDYLD::GetTargetExecutable() +{ + Target &target = m_process->GetTarget(); + ModuleSP executable = target.GetExecutableModule(); + + if (executable.get()) + { + if (executable->GetFileSpec().Exists()) + { + 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 (module_sp->GetUUID() != executable->GetUUID()) + executable.reset(); + } + else if (executable->FileHasChanged()) + { + executable.reset(); + } + + if (!executable.get()) + { + executable = target.GetSharedModule(module_spec); + 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 + const bool get_dependent_images = false; + target.SetExecutableModule(executable, get_dependent_images); + } + } + } + } + return executable; +} + +Error +DynamicLoaderPOSIXDYLD::ExecutePluginCommand(Args &command, Stream *strm) +{ + return Error(); +} + +Log * +DynamicLoaderPOSIXDYLD::EnablePluginLogging(Stream *strm, Args &command) +{ + return NULL; +} + +Error +DynamicLoaderPOSIXDYLD::CanLoadImage() +{ + return Error(); +} + +void +DynamicLoaderPOSIXDYLD::UpdateLoadedSections(ModuleSP module, addr_t base_addr) +{ + ObjectFile *obj_file = module->GetObjectFile(); + SectionList *sections = obj_file->GetSectionList(); + SectionLoadList &load_list = m_process->GetTarget().GetSectionLoadList(); + 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; + lldb::addr_t old_load_addr = load_list.GetSectionLoadAddress(section_sp); + + // 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; + + if (old_load_addr == LLDB_INVALID_ADDRESS || + old_load_addr != new_load_addr) + load_list.SetSectionLoadAddress(section_sp, new_load_addr); + } +} + +void +DynamicLoaderPOSIXDYLD::ProbeEntry() +{ + Breakpoint *entry_break; + addr_t entry; + + if ((entry = GetEntryPoint()) == LLDB_INVALID_ADDRESS) + return; + + entry_break = m_process->GetTarget().CreateBreakpoint(entry, true).get(); + entry_break->SetCallback(EntryBreakpointHit, this, true); + entry_break->SetBreakpointKind("shared-library-event"); +} + +// 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) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast(baton); + dyld_instance->LoadAllCurrentModules(); + dyld_instance->SetRendezvousBreakpoint(); + return false; // Continue running. +} + +void +DynamicLoaderPOSIXDYLD::SetRendezvousBreakpoint() +{ + addr_t break_addr = m_rendezvous.GetBreakAddress(); + Target &target = m_process->GetTarget(); + + if (m_dyld_bid == LLDB_INVALID_BREAK_ID) + { + Breakpoint *dyld_break = target.CreateBreakpoint (break_addr, true).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); +} + +bool +DynamicLoaderPOSIXDYLD::RendezvousBreakpointHit(void *baton, + StoppointCallbackContext *context, + user_id_t break_id, + user_id_t break_loc_id) +{ + DynamicLoaderPOSIXDYLD* dyld_instance; + + dyld_instance = static_cast(baton); + dyld_instance->RefreshModules(); + + // Return true to stop the target, false to just let the target run. + return dyld_instance->GetStopWhenImagesChange(); +} + +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) + { + FileSpec file(I->path.c_str(), true); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (module_sp.get()) + loaded_modules.AppendIfNeeded(module_sp); + } + } + + 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.c_str(), true); + ModuleSpec module_spec (file); + ModuleSP module_sp = + loaded_modules.FindFirstModule (module_spec); + if (module_sp.get()) + old_modules.Append(module_sp); + } + loaded_modules.Remove(old_modules); + } +} + +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 == NULL || !sym->IsTrampoline()) + return thread_plan_sp; + + const ConstString &sym_name = sym->GetMangled().GetName(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 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(); + + std::sort(start, end); + addrs.erase(std::unique(start, end), end); + thread_plan_sp.reset(new ThreadPlanRunToAddress(thread, addrs, stop)); + } + + return thread_plan_sp; +} + +void +DynamicLoaderPOSIXDYLD::LoadAllCurrentModules() +{ + DYLDRendezvous::iterator I; + DYLDRendezvous::iterator E; + ModuleList module_list; + + if (!m_rendezvous.Resolve()) + return; + + for (I = m_rendezvous.begin(), E = m_rendezvous.end(); I != E; ++I) + { + FileSpec file(I->path.c_str(), false); + ModuleSP module_sp = LoadModuleAtAddress(file, I->base_addr); + if (module_sp.get()) + module_list.Append(module_sp); + } + + m_process->GetTarget().ModulesDidLoad(module_list); +} + +ModuleSP +DynamicLoaderPOSIXDYLD::LoadModuleAtAddress(const FileSpec &file, addr_t base_addr) +{ + Target &target = m_process->GetTarget(); + ModuleList &modules = target.GetImages(); + ModuleSP module_sp; + + ModuleSpec module_spec (file, target.GetArchitecture()); + if ((module_sp = modules.FindFirstModule (module_spec))) + { + UpdateLoadedSections(module_sp, base_addr); + } + else if ((module_sp = target.GetSharedModule(module_spec))) + { + UpdateLoadedSections(module_sp, base_addr); + } + + return module_sp; +} + +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(); + 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; +} + +addr_t +DynamicLoaderPOSIXDYLD::GetEntryPoint() +{ + if (m_entry_point != LLDB_INVALID_ADDRESS) + return m_entry_point; + + if (m_auxv.get() == NULL) + return LLDB_INVALID_ADDRESS; + + AuxVector::iterator I = m_auxv->FindEntry(AuxVector::AT_ENTRY); + + if (I == m_auxv->end()) + return LLDB_INVALID_ADDRESS; + + m_entry_point = static_cast(I->value); + return m_entry_point; +} diff --git a/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h new file mode 100644 index 00000000000..0476e45d046 --- /dev/null +++ b/source/Plugins/DynamicLoader/POSIX-DYLD/DynamicLoaderPOSIXDYLD.h @@ -0,0 +1,170 @@ +//===-- DynamicLoaderPOSIX.h ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderPOSIX_H_ +#define liblldb_DynamicLoaderPOSIX_H_ + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Target/DynamicLoader.h" + +#include "DYLDRendezvous.h" + +class AuxVector; + +class DynamicLoaderPOSIXDYLD : public lldb_private::DynamicLoader +{ +public: + + 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); + + DynamicLoaderPOSIXDYLD(lldb_private::Process *process); + + virtual + ~DynamicLoaderPOSIXDYLD(); + + //------------------------------------------------------------------ + // DynamicLoader protocol + //------------------------------------------------------------------ + + virtual void + DidAttach(); + + virtual void + DidLaunch(); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan(lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage(); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp(const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand(lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging(lldb_private::Stream *strm, lldb_private::Args &command); + +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 m_auxv; + + /// Rendezvous breakpoint. + lldb::break_id_t m_dyld_bid; + + /// Enables a breakpoint on a function called by the runtime + /// linker each time a module is loaded or unloaded. + void + 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 base_addr The virtual base address @p module is loaded at. + void + UpdateLoadedSections(lldb::ModuleSP module, + lldb::addr_t base_addr = 0); + + /// Locates or creates a module given by @p file and updates/loads the + /// resulting module at the virtual base address @p base_addr. + lldb::ModuleSP + LoadModuleAtAddress(const lldb_private::FileSpec &file, lldb::addr_t base_addr); + + /// 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. + 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(); + +private: + DISALLOW_COPY_AND_ASSIGN(DynamicLoaderPOSIXDYLD); +}; + +#endif // liblldb_DynamicLoaderPOSIXDYLD_H_ diff --git a/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp new file mode 100644 index 00000000000..274ba328ad1 --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.cpp @@ -0,0 +1,209 @@ +//===-- DynamicLoaderStatic.cpp ---------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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 NULL; +} + +//---------------------------------------------------------------------- +// 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); + + Mutex::Locker mutex_locker(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().GetSectionLoadList().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(); +} + +Error +DynamicLoaderStatic::CanLoadImage () +{ + Error 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/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h new file mode 100644 index 00000000000..a99435fa32a --- /dev/null +++ b/source/Plugins/DynamicLoader/Static/DynamicLoaderStatic.h @@ -0,0 +1,88 @@ +//===-- DynamicLoaderStatic.h -----------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_DynamicLoaderStatic_h_ +#define liblldb_DynamicLoaderStatic_h_ + +// C Includes +// C++ Includes +#include +#include +#include + +// Other libraries and framework includes +#include "llvm/Support/MachO.h" + +#include "lldb/Target/DynamicLoader.h" +#include "lldb/Host/FileSpec.h" +#include "lldb/Core/UUID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" + +class DynamicLoaderStatic : public lldb_private::DynamicLoader +{ +public: + //------------------------------------------------------------------ + // 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); + + DynamicLoaderStatic (lldb_private::Process *process); + + virtual + ~DynamicLoaderStatic (); + //------------------------------------------------------------------ + /// Called after attaching a process. + /// + /// Allow DynamicLoader plug-ins to execute some code after + /// attaching to a process. + //------------------------------------------------------------------ + virtual void + DidAttach (); + + virtual void + DidLaunch (); + + virtual lldb::ThreadPlanSP + GetStepThroughTrampolinePlan (lldb_private::Thread &thread, + bool stop_others); + + virtual lldb_private::Error + CanLoadImage (); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual lldb_private::ConstString + GetPluginName(); + + virtual uint32_t + GetPluginVersion(); + +private: + void + LoadAllImagesAtFileAddresses (); + + DISALLOW_COPY_AND_ASSIGN (DynamicLoaderStatic); +}; + +#endif // liblldb_DynamicLoaderStatic_h_ diff --git a/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp b/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp new file mode 100644 index 00000000000..2dd04dd8733 --- /dev/null +++ b/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp @@ -0,0 +1,13625 @@ +//===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#include "EmulateInstructionARM.h" +#include "EmulationStateARM.h" +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/Address.h" +#include "lldb/Core/ConstString.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Stream.h" +#include "lldb/Interpreter/OptionValueArray.h" +#include "lldb/Interpreter/OptionValueDictionary.h" +#include "lldb/Symbol/UnwindPlan.h" + +#include "Plugins/Process/Utility/ARMDefines.h" +#include "Plugins/Process/Utility/ARMUtils.h" +#include "Utility/ARM_DWARF_Registers.h" + +#include "llvm/Support/MathExtras.h" // for SignExtend32 template function + // and countTrailingZeros function + +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 +// +//---------------------------------------------------------------------- + +// 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::SupportsEmulatingIntructionsOfTypeStatic(inst_type)) + { + if (arch.GetTriple().getArch() == llvm::Triple::arm) + { + std::unique_ptr emulate_insn_ap (new EmulateInstructionARM (arch)); + + if (emulate_insn_ap.get()) + return emulate_insn_ap.release(); + } + else if (arch.GetTriple().getArch() == llvm::Triple::thumb) + { + std::unique_ptr emulate_insn_ap (new EmulateInstructionARM (arch)); + + if (emulate_insn_ap.get()) + return emulate_insn_ap.release(); + } + } + + return NULL; +} + +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(); + + if (!MemAWrite (context, address, random_data, addr_byte_size)) + return false; + + return true; +} + +// 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 (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_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_opcode_mode == eModeThumb) + { + switch (m_arch.GetTriple().getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + return 7; + default: + break; + } + } + return 11; +} + +uint32_t +EmulateInstructionARM::GetFramePointerDWARFRegisterNumber () const +{ + if (m_opcode_mode == eModeThumb) + { + switch (m_arch.GetTriple().getOS()) + { + case llvm::Triple::Darwin: + case llvm::Triple::MacOSX: + case llvm::Triple::IOS: + return dwarf_r7; + default: + break; + } + } + return dwarf_r11; +} + +// 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 == '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 conditional = false; + bool success = false; + if (ConditionPassed(opcode, &conditional)) + { + 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; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + 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 == '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; + + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + 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; + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + + for (i=0; i<15; ++i) + { + if (BitIsSet (registers, i)) + { + context.SetRegisterPlusOffset (sp_reg, addr - sp); + 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; + context.type = eContextSetFramePointer; + 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; + context.type = EmulateInstruction::eContextRegisterLoad; + RegisterInfo dwarf_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); + context.SetRegister (dwarf_reg); + + 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 = false; // Add this back if/when support eEncodingT3 eEncodingA1 + 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); + + break; + + case eEncodingT2: + // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32); + d = 13; + imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) + + 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::eContextAdjustStackPointer; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + context.SetRegisterPlusOffset (sp_reg, sp_offset); + + if (d == 15) + { + if (!ALUWritePC (context, addr)) + return false; + } + else + { + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, addr)) + return false; + + // Add this back if/when support eEncodingT3 eEncodingA1 + //if (setflags) + //{ + // APSR.N = result<31>; + // APSR.Z = IsZeroBit(result); + // APSR.C = carry; + // APSR.V = overflow; + //} + } + } + 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; + 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; + 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; + 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; + 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; + } + 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 conditional = false; + bool success = false; + if (ConditionPassed(opcode, &conditional)) + { + 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; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + 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; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + 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; + if (conditional) + context.type = EmulateInstruction::eContextRegisterStore; + else + context.type = EmulateInstruction::eContextPushRegisterOnStack; + RegisterInfo dwarf_reg; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i 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 accomodate 64-bit registers. + + EmulateInstruction::Context context; + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + RegisterInfo dwarf_reg; + RegisterInfo sp_reg; + GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); + for (i=0; i = 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)); + target = pc + imm32; + context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); + break; + case eEncodingT3: + // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). + { + 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 (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; + + 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(Rm, &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); + // if Rn == '1101' then SEE ADD (SP plus 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 BadReg(d) || n == 15 then UNPREDICTABLE; + if (BadReg (d) || (n == 15)) + return false; + + break; + + case eEncodingT4: + { + // if Rn == '1111' then SEE ADR; + // if Rn == '1101' then SEE ADD (SP plus immediate); + // 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 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; + context.type = eContextArithmetic; + 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 (); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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(); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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 (); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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 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(); + if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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 == '1' then + R[i] = MemA[address, 4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC (MemA[address, 4]); + + if wback && registers == '0' then R[n] = R[n] + 4 * BitCount (registers); + if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 + +#endif + + bool success = false; + bool conditional = false; + if (ConditionPassed(opcode, &conditional)) + { + 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 == '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 == '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 + { + if (conditional) + context.type = EmulateInstruction::eContextRegisterLoad; + else + context.type = EmulateInstruction::eContextPopRegisterOffStack; + } + + // 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 == '1' then + R[i] = MemA[address,4]; address = address + 4; + + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers == '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 == '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 == '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 == '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 lcoations 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 == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); + if wback && registers == '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 == '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 == '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 == '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 == '1' then + R[i] = MemA[address,4]; address = address + 4; + if registers<15> == '1' then + LoadWritePC(MemA[address,4]); + + if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); + if wback && registers == '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 == '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 == '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; + 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 teh 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 == '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 == '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 == '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 == '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 == '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 == '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 == '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 == '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 == '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 == '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) calcualtes 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; + 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) + { + context.type = eContextRegisterLoad; + 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 alue 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 resgister. 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: + // if Rt == '1111' then SEE PLD; + // if Rn == '1111' then SEE LDRB (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 Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD; + // if Rn == '1111' then SEE LDRB (literal); + // 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 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: + // if Rt == '1111' then SEE PLD; + // 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(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: + // if Rt == '1111' then SEE PLD; + // if Rn == '1111' then SEE LDRB (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 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 (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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 (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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 (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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(); + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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 (); + + if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) + return false; + + return true; +} + +// 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; + + 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; + context.type = EmulateInstruction::eContextImmediate; + context.SetNoArgs (); + + 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) || (m == 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: + // if Rd == Ô1111Õ && S == Ô1Õ then SEE CMP (register); + // if Rn == Ô1101Õ then SEE SUB (SP minus register); + // 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); + + // (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; + } + else + { + // R[d] = 1; + if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) + return false; + } + } + 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; + context.type = eContextRegisterLoad; + context.SetRegisterPlusOffset (base_reg, address - Rn); + + 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.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); + 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; + context.type = eContextRegisterLoad; + context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); + + // 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; + 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) + { + 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; + 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 instruciton loads a single extension register fronm 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 regsiters, 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, #" }, + { 0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push " }, + + // set r7 to point to a stack offset + { 0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #" }, + { 0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #"}, + // 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, #" }, + { 0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #"}, + + // adjust the stack pointer + { 0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #"}, + { 0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPReg, "sub{s} , sp, {,}" }, + + // 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 "}, + { 0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 "}, + + //---------------------------------------------------------------------- + // Epilogue instructions + //---------------------------------------------------------------------- + + { 0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, + { 0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, + { 0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 "}, + { 0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 "}, + + //---------------------------------------------------------------------- + // Supervisor Call (previously Software Interrupt) + //---------------------------------------------------------------------- + { 0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSVC, "svc #imm24"}, + + //---------------------------------------------------------------------- + // Branch instructions + //---------------------------------------------------------------------- + { 0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b #imm24"}, + // To resolve ambiguity, "blx